| /* |
| * 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 <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 */, "_") { |
| 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)) return "int" + bits; |
| if (IsFloat(type.base_type)) return "float" + bits; |
| if (type.base_type == BASE_TYPE_STRING) 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 (IsScalar(type.base_type) && type.enum_def) return NormalizedName(*type.enum_def); |
| if (!IsScalar(type.base_type)) return "flatbuffers_offset"; |
| return "int"; |
| } |
| |
| // Returns the method name for use with add/put calls. |
| std::string GenMethod(const Type &type) { |
| return IsScalar(type.base_type) |
| ? MakeCamel(GenTypeBasic(type)) |
| : (IsStruct(type) ? "Struct" : "UOffsetTRelative"); |
| } |
| |
| // This uses Python names for now.. |
| std::string GenTypeBasic(const Type &type) { |
| static const char *ctypename[] = { |
| // clang-format off |
| #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ |
| CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \ |
| #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 { |
| acc = "buf_.flatbuffers_field_" + |
| GenTypeName(field.value.type) + "(pos_, " + offsets + ", " + |
| field.value.constant + ")"; |
| } |
| if (field.value.type.enum_def) |
| acc = NormalizedName(*field.value.type.enum_def) + "(" + acc + ")"; |
| code += def + "():\n return " + acc + "\n"; |
| return; |
| } |
| switch (field.value.type.base_type) { |
| case BASE_TYPE_STRUCT: { |
| auto name = NamespacedName(*field.value.type.struct_def); |
| code += def + "():\n "; |
| if (struct_def.fixed) { |
| code += "return " + name + "{ buf_, pos_ + " + offsets + " }\n"; |
| } else { |
| code += std::string("let o = buf_.flatbuffers_field_") + |
| (field.value.type.struct_def->fixed ? "struct" : "table") + |
| "(pos_, " + offsets + ")\n return if o: " + name + |
| " { buf_, o } else: nil\n"; |
| } |
| break; |
| } |
| case BASE_TYPE_STRING: |
| code += def + "():\n return buf_.flatbuffers_field_string(pos_, " + |
| offsets + ")\n"; |
| break; |
| case BASE_TYPE_VECTOR: { |
| auto vectortype = field.value.type.VectorType(); |
| code += def + "(i:int):\n return "; |
| if (vectortype.base_type == BASE_TYPE_STRUCT) { |
| auto start = "buf_.flatbuffers_field_vector(pos_, " + offsets + |
| ") + i * " + NumToString(InlineSize(vectortype)); |
| if (!(vectortype.struct_def->fixed)) { |
| start = "buf_.flatbuffers_indirect(" + start + ")"; |
| } |
| code += NamespacedName(*field.value.type.struct_def) + " { buf_, " + |
| start + " }\n"; |
| } else { |
| if (vectortype.base_type == BASE_TYPE_STRING) |
| code += "buf_.flatbuffers_string"; |
| else |
| code += "buf_.read_" + GenTypeName(vectortype) + "_le"; |
| code += "(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_, buf_.flatbuffers_field_table(pos_, " + offsets + |
| ") }\n"; |
| } |
| } |
| break; |
| } |
| default: FLATBUFFERS_ASSERT(0); |
| } |
| if (field.value.type.base_type == BASE_TYPE_VECTOR) { |
| code += def + |
| "_length():\n return buf_.flatbuffers_field_vector_len(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)) |
| 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 (field.value.type.base_type == BASE_TYPE_VECTOR) { |
| code += "def " + NormalizedName(struct_def) + "Start" + |
| MakeCamel(NormalizedName(field)) + |
| "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" + |
| MakeCamel(NormalizedName(field)) + |
| "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, buf.flatbuffers_indirect(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 SaveFile((path_ + file_name_ + "_generated.lobster").c_str(), |
| code, false); |
| } |
| |
| private: |
| std::unordered_set<std::string> keywords_; |
| std::string current_namespace_; |
| }; |
| |
| } // namespace lobster |
| |
| 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 flatbuffers |