| /* | |
| * Copyright 2018 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 "idl_gen_lobster.h" | |
| #include <string> | |
| #include <unordered_set> | |
| #include "flatbuffers/code_generators.h" | |
| #include "flatbuffers/flatbuffers.h" | |
| #include "flatbuffers/idl.h" | |
| #include "flatbuffers/util.h" | |
| namespace flatbuffers { | |
| namespace lobster { | |
| class LobsterGenerator : public BaseGenerator { | |
| public: | |
| LobsterGenerator(const Parser& parser, const std::string& path, | |
| const std::string& file_name) | |
| : BaseGenerator(parser, path, file_name, "" /* not used */, ".", | |
| "lobster") { | |
| static const char* const keywords[] = { | |
| "nil", "true", "false", "return", "struct", "class", | |
| "import", "int", "float", "string", "any", "def", | |
| "is", "from", "program", "private", "coroutine", "resource", | |
| "enum", "typeof", "var", "let", "pakfile", "switch", | |
| "case", "default", "namespace", "not", "and", "or", | |
| "bool", | |
| }; | |
| keywords_.insert(std::begin(keywords), std::end(keywords)); | |
| } | |
| std::string EscapeKeyword(const std::string& name) const { | |
| return keywords_.find(name) == keywords_.end() ? name : name + "_"; | |
| } | |
| std::string NormalizedName(const Definition& definition) const { | |
| return EscapeKeyword(definition.name); | |
| } | |
| std::string NormalizedName(const EnumVal& ev) const { | |
| return EscapeKeyword(ev.name); | |
| } | |
| std::string NamespacedName(const Definition& def) { | |
| return WrapInNameSpace(def.defined_namespace, NormalizedName(def)); | |
| } | |
| std::string GenTypeName(const Type& type) { | |
| auto bits = NumToString(SizeOf(type.base_type) * 8); | |
| if (IsInteger(type.base_type)) { | |
| if (IsUnsigned(type.base_type)) | |
| return "uint" + bits; | |
| else | |
| return "int" + bits; | |
| } | |
| if (IsFloat(type.base_type)) return "float" + bits; | |
| if (IsString(type)) return "string"; | |
| if (type.base_type == BASE_TYPE_STRUCT) return "table"; | |
| return "none"; | |
| } | |
| std::string LobsterType(const Type& type) { | |
| if (IsFloat(type.base_type)) return "float"; | |
| if (IsBool(type.base_type)) return "bool"; | |
| if (IsScalar(type.base_type) && type.enum_def) | |
| return NormalizedName(*type.enum_def); | |
| if (!IsScalar(type.base_type)) return "flatbuffers.offset"; | |
| if (IsString(type)) return "string"; | |
| return "int"; | |
| } | |
| // Returns the method name for use with add/put calls. | |
| std::string GenMethod(const Type& type) { | |
| return IsScalar(type.base_type) | |
| ? ConvertCase(GenTypeBasic(type), Case::kUpperCamel) | |
| : (IsStruct(type) ? "Struct" : "UOffsetTRelative"); | |
| } | |
| // This uses Python names for now.. | |
| std::string GenTypeBasic(const Type& type) { | |
| // clang-format off | |
| static const char *ctypename[] = { | |
| #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ | |
| CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, ...) \ | |
| #PTYPE, | |
| FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) | |
| #undef FLATBUFFERS_TD | |
| }; | |
| // clang-format on | |
| return ctypename[type.base_type]; | |
| } | |
| // Generate a struct field, conditioned on its child type(s). | |
| void GenStructAccessor(const StructDef& struct_def, const FieldDef& field, | |
| std::string* code_ptr) { | |
| GenComment(field.doc_comment, code_ptr, nullptr, " "); | |
| std::string& code = *code_ptr; | |
| auto offsets = NumToString(field.value.offset); | |
| auto def = " def " + NormalizedName(field); | |
| if (IsScalar(field.value.type.base_type)) { | |
| std::string acc; | |
| if (struct_def.fixed) { | |
| acc = "buf_.read_" + GenTypeName(field.value.type) + "_le(pos_ + " + | |
| offsets + ")"; | |
| } else { | |
| auto defval = field.IsOptional() | |
| ? (IsFloat(field.value.type.base_type) ? "0.0" : "0") | |
| : field.value.constant; | |
| acc = "flatbuffers.field_" + GenTypeName(field.value.type) + | |
| "(buf_, pos_, " + offsets + ", " + defval + ")"; | |
| if (IsBool(field.value.type.base_type)) acc = "bool(" + acc + ")"; | |
| } | |
| if (field.value.type.enum_def) | |
| acc = NormalizedName(*field.value.type.enum_def) + "(" + acc + ")"; | |
| if (field.IsOptional()) { | |
| acc += ", flatbuffers.field_present(buf_, pos_, " + offsets + ")"; | |
| code += def + "() -> " + LobsterType(field.value.type) + | |
| ", bool:\n return " + acc + "\n"; | |
| } else { | |
| code += def + "() -> " + LobsterType(field.value.type) + | |
| ":\n return " + acc + "\n"; | |
| } | |
| return; | |
| } | |
| switch (field.value.type.base_type) { | |
| case BASE_TYPE_STRUCT: { | |
| auto name = NamespacedName(*field.value.type.struct_def); | |
| if (struct_def.fixed) { | |
| code += def + "() -> " + name + ":\n "; | |
| code += "return " + name + "{ buf_, pos_ + " + offsets + " }\n"; | |
| } else { | |
| code += def + "() -> " + name; | |
| if (!field.IsRequired()) code += "?"; | |
| code += ":\n "; | |
| code += std::string("let o = flatbuffers.field_") + | |
| (field.value.type.struct_def->fixed ? "struct" : "table") + | |
| "(buf_, pos_, " + offsets + ")\n return "; | |
| if (field.IsRequired()) { | |
| code += name + " { buf_, assert o }\n"; | |
| } else { | |
| code += "if o: " + name + " { buf_, o } else: nil\n"; | |
| } | |
| } | |
| break; | |
| } | |
| case BASE_TYPE_STRING: | |
| code += def + | |
| "() -> string:\n return " | |
| "flatbuffers.field_string(buf_, pos_, " + | |
| offsets + ")\n"; | |
| break; | |
| case BASE_TYPE_VECTOR: { | |
| auto vectortype = field.value.type.VectorType(); | |
| if (vectortype.base_type == BASE_TYPE_STRUCT) { | |
| auto start = "flatbuffers.field_vector(buf_, pos_, " + offsets + | |
| ") + i * " + NumToString(InlineSize(vectortype)); | |
| if (!(vectortype.struct_def->fixed)) { | |
| start = "flatbuffers.indirect(buf_, " + start + ")"; | |
| } | |
| code += def + "(i:int) -> " + | |
| NamespacedName(*field.value.type.struct_def) + | |
| ":\n return "; | |
| code += NamespacedName(*field.value.type.struct_def) + " { buf_, " + | |
| start + " }\n"; | |
| } else { | |
| if (IsString(vectortype)) { | |
| code += def + "(i:int) -> string:\n return "; | |
| code += "flatbuffers.string"; | |
| } else { | |
| code += def + "(i:int) -> " + LobsterType(vectortype) + | |
| ":\n return "; | |
| code += "read_" + GenTypeName(vectortype) + "_le"; | |
| } | |
| code += "(buf_, buf_.flatbuffers.field_vector(pos_, " + offsets + | |
| ") + i * " + NumToString(InlineSize(vectortype)) + ")\n"; | |
| } | |
| break; | |
| } | |
| case BASE_TYPE_UNION: { | |
| for (auto it = field.value.type.enum_def->Vals().begin(); | |
| it != field.value.type.enum_def->Vals().end(); ++it) { | |
| auto& ev = **it; | |
| if (ev.IsNonZero()) { | |
| code += def + "_as_" + ev.name + "():\n return " + | |
| NamespacedName(*ev.union_type.struct_def) + | |
| " { buf_, flatbuffers.field_table(buf_, pos_, " + offsets + | |
| ") }\n"; | |
| } | |
| } | |
| break; | |
| } | |
| default: | |
| FLATBUFFERS_ASSERT(0); | |
| } | |
| if (IsVector(field.value.type)) { | |
| code += def + | |
| "_length() -> int:\n return " | |
| "flatbuffers.field_vector_len(buf_, pos_, " + | |
| offsets + ")\n"; | |
| } | |
| } | |
| // Generate table constructors, conditioned on its members' types. | |
| void GenTableBuilders(const StructDef& struct_def, std::string* code_ptr) { | |
| std::string& code = *code_ptr; | |
| code += "struct " + NormalizedName(struct_def) + | |
| "Builder:\n b_:flatbuffers.builder\n"; | |
| code += " def start():\n b_.StartObject(" + | |
| NumToString(struct_def.fields.vec.size()) + | |
| ")\n 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; | |
| auto offset = it - struct_def.fields.vec.begin(); | |
| code += " def add_" + NormalizedName(field) + "(" + | |
| NormalizedName(field) + ":" + LobsterType(field.value.type) + | |
| "):\n b_.Prepend" + GenMethod(field.value.type) + "Slot(" + | |
| NumToString(offset) + ", " + NormalizedName(field); | |
| if (IsScalar(field.value.type.base_type) && !field.IsOptional()) | |
| code += ", " + field.value.constant; | |
| code += ")\n return this\n"; | |
| } | |
| code += " def end():\n return b_.EndObject()\n\n"; | |
| for (auto it = struct_def.fields.vec.begin(); | |
| it != struct_def.fields.vec.end(); ++it) { | |
| auto& field = **it; | |
| if (field.deprecated) continue; | |
| if (IsVector(field.value.type)) { | |
| code += "def " + NormalizedName(struct_def) + "Start" + | |
| ConvertCase(NormalizedName(field), Case::kUpperCamel) + | |
| "Vector(b_:flatbuffers.builder, n_:int):\n b_.StartVector("; | |
| auto vector_type = field.value.type.VectorType(); | |
| auto alignment = InlineAlignment(vector_type); | |
| auto elem_size = InlineSize(vector_type); | |
| code += | |
| NumToString(elem_size) + ", n_, " + NumToString(alignment) + ")\n"; | |
| if (vector_type.base_type != BASE_TYPE_STRUCT || | |
| !vector_type.struct_def->fixed) { | |
| code += "def " + NormalizedName(struct_def) + "Create" + | |
| ConvertCase(NormalizedName(field), Case::kUpperCamel) + | |
| "Vector(b_:flatbuffers.builder, v_:[" + | |
| LobsterType(vector_type) + "]):\n b_.StartVector(" + | |
| NumToString(elem_size) + ", v_.length, " + | |
| NumToString(alignment) + ")\n reverse(v_) e_: b_.Prepend" + | |
| GenMethod(vector_type) + | |
| "(e_)\n return b_.EndVector(v_.length)\n"; | |
| } | |
| code += "\n"; | |
| } | |
| } | |
| } | |
| void GenStructPreDecl(const StructDef& struct_def, std::string* code_ptr) { | |
| if (struct_def.generated) return; | |
| std::string& code = *code_ptr; | |
| CheckNameSpace(struct_def, &code); | |
| code += "class " + NormalizedName(struct_def) + "\n\n"; | |
| } | |
| // Generate struct or table methods. | |
| void GenStruct(const StructDef& struct_def, std::string* code_ptr) { | |
| if (struct_def.generated) return; | |
| std::string& code = *code_ptr; | |
| CheckNameSpace(struct_def, &code); | |
| GenComment(struct_def.doc_comment, code_ptr, nullptr, ""); | |
| code += "class " + NormalizedName(struct_def) + " : flatbuffers.handle\n"; | |
| for (auto it = struct_def.fields.vec.begin(); | |
| it != struct_def.fields.vec.end(); ++it) { | |
| auto& field = **it; | |
| if (field.deprecated) continue; | |
| GenStructAccessor(struct_def, field, code_ptr); | |
| } | |
| code += "\n"; | |
| if (!struct_def.fixed) { | |
| // Generate a special accessor for the table that has been declared as | |
| // the root type. | |
| code += "def GetRootAs" + NormalizedName(struct_def) + | |
| "(buf:string): return " + NormalizedName(struct_def) + | |
| " { buf, flatbuffers.indirect(buf, 0) }\n\n"; | |
| } | |
| if (struct_def.fixed) { | |
| // create a struct constructor function | |
| GenStructBuilder(struct_def, code_ptr); | |
| } else { | |
| // Create a set of functions that allow table construction. | |
| GenTableBuilders(struct_def, code_ptr); | |
| } | |
| } | |
| // Generate enum declarations. | |
| void GenEnum(const EnumDef& enum_def, std::string* code_ptr) { | |
| if (enum_def.generated) return; | |
| std::string& code = *code_ptr; | |
| CheckNameSpace(enum_def, &code); | |
| GenComment(enum_def.doc_comment, code_ptr, nullptr, ""); | |
| code += "enum " + NormalizedName(enum_def) + ":\n"; | |
| for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { | |
| auto& ev = **it; | |
| GenComment(ev.doc_comment, code_ptr, nullptr, " "); | |
| code += " " + enum_def.name + "_" + NormalizedName(ev) + " = " + | |
| enum_def.ToString(ev) + "\n"; | |
| } | |
| code += "\n"; | |
| } | |
| // Recursively generate arguments for a constructor, to deal with nested | |
| // structs. | |
| void StructBuilderArgs(const StructDef& struct_def, const char* nameprefix, | |
| std::string* 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 field name. | |
| StructBuilderArgs(*field.value.type.struct_def, | |
| (nameprefix + (NormalizedName(field) + "_")).c_str(), | |
| code_ptr); | |
| } else { | |
| std::string& code = *code_ptr; | |
| code += ", " + (nameprefix + NormalizedName(field)) + ":" + | |
| LobsterType(field.value.type); | |
| } | |
| } | |
| } | |
| // Recursively generate struct construction statements and instert manual | |
| // padding. | |
| void StructBuilderBody(const StructDef& struct_def, const char* nameprefix, | |
| std::string* code_ptr) { | |
| std::string& code = *code_ptr; | |
| code += " b_.Prep(" + NumToString(struct_def.minalign) + ", " + | |
| NumToString(struct_def.bytesize) + ")\n"; | |
| for (auto it = struct_def.fields.vec.rbegin(); | |
| it != struct_def.fields.vec.rend(); ++it) { | |
| auto& field = **it; | |
| if (field.padding) | |
| code += " b_.Pad(" + NumToString(field.padding) + ")\n"; | |
| if (IsStruct(field.value.type)) { | |
| StructBuilderBody(*field.value.type.struct_def, | |
| (nameprefix + (NormalizedName(field) + "_")).c_str(), | |
| code_ptr); | |
| } else { | |
| code += " b_.Prepend" + GenMethod(field.value.type) + "(" + | |
| nameprefix + NormalizedName(field) + ")\n"; | |
| } | |
| } | |
| } | |
| // Create a struct with a builder and the struct's arguments. | |
| void GenStructBuilder(const StructDef& struct_def, std::string* code_ptr) { | |
| std::string& code = *code_ptr; | |
| code += | |
| "def Create" + NormalizedName(struct_def) + "(b_:flatbuffers.builder"; | |
| StructBuilderArgs(struct_def, "", code_ptr); | |
| code += "):\n"; | |
| StructBuilderBody(struct_def, "", code_ptr); | |
| code += " return b_.Offset()\n\n"; | |
| } | |
| void CheckNameSpace(const Definition& def, std::string* code_ptr) { | |
| auto ns = GetNameSpace(def); | |
| if (ns == current_namespace_) return; | |
| current_namespace_ = ns; | |
| std::string& code = *code_ptr; | |
| code += "namespace " + ns + "\n\n"; | |
| } | |
| bool generate() { | |
| std::string code; | |
| code += std::string("// ") + FlatBuffersGeneratedWarning() + | |
| "\nimport flatbuffers\n\n"; | |
| for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); | |
| ++it) { | |
| auto& enum_def = **it; | |
| GenEnum(enum_def, &code); | |
| } | |
| for (auto it = parser_.structs_.vec.begin(); | |
| it != parser_.structs_.vec.end(); ++it) { | |
| auto& struct_def = **it; | |
| GenStructPreDecl(struct_def, &code); | |
| } | |
| for (auto it = parser_.structs_.vec.begin(); | |
| it != parser_.structs_.vec.end(); ++it) { | |
| auto& struct_def = **it; | |
| GenStruct(struct_def, &code); | |
| } | |
| return parser_.opts.file_saver->SaveFile( | |
| GeneratedFileName(path_, file_name_, parser_.opts).c_str(), code, | |
| false); | |
| } | |
| private: | |
| std::unordered_set<std::string> keywords_; | |
| std::string current_namespace_; | |
| }; | |
| } // namespace lobster | |
| static bool GenerateLobster(const Parser& parser, const std::string& path, | |
| const std::string& file_name) { | |
| lobster::LobsterGenerator generator(parser, path, file_name); | |
| return generator.generate(); | |
| } | |
| namespace { | |
| class LobsterCodeGenerator : public CodeGenerator { | |
| public: | |
| Status GenerateCode(const Parser& parser, const std::string& path, | |
| const std::string& filename) override { | |
| if (!GenerateLobster(parser, path, filename)) { | |
| return Status::ERROR; | |
| } | |
| return Status::OK; | |
| } | |
| Status GenerateCode(const uint8_t*, int64_t, const CodeGenOptions&) 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 false; } | |
| bool SupportsRootFileGeneration() const override { return false; } | |
| IDLOptions::Language Language() const override { | |
| return IDLOptions::kLobster; | |
| } | |
| std::string LanguageName() const override { return "Lobster"; } | |
| }; | |
| } // namespace | |
| std::unique_ptr<CodeGenerator> NewLobsterCodeGenerator() { | |
| return std::unique_ptr<LobsterCodeGenerator>(new LobsterCodeGenerator()); | |
| } | |
| } // namespace flatbuffers |