Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright 2018 Google Inc. All rights reserved.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <string>
     18 #include <unordered_set>
     19 
     20 #include "flatbuffers/code_generators.h"
     21 #include "flatbuffers/flatbuffers.h"
     22 #include "flatbuffers/idl.h"
     23 #include "flatbuffers/util.h"
     24 
     25 namespace flatbuffers {
     26 namespace lobster {
     27 
     28 class LobsterGenerator : public BaseGenerator {
     29  public:
     30  LobsterGenerator(const Parser &parser, const std::string &path,
     31                   const std::string &file_name)
     32       : BaseGenerator(parser, path, file_name, "" /* not used */, "_") {
     33     static const char * const keywords[] = {
     34       "nil", "true", "false", "return", "struct", "value", "include", "int",
     35       "float", "string", "any", "def", "is", "from", "program", "private",
     36       "coroutine", "resource", "enum", "typeof", "var", "let", "pakfile",
     37       "switch", "case", "default", "namespace", "not", "and", "or", "bool",
     38     };
     39     keywords_.insert(std::begin(keywords), std::end(keywords));
     40   }
     41 
     42   std::string EscapeKeyword(const std::string &name) const {
     43     return keywords_.find(name) == keywords_.end() ? name : name + "_";
     44   }
     45 
     46   std::string NormalizedName(const Definition &definition) const {
     47     return EscapeKeyword(definition.name);
     48   }
     49 
     50   std::string NormalizedName(const EnumVal &ev) const {
     51     return EscapeKeyword(ev.name);
     52   }
     53 
     54   std::string NamespacedName(const Definition &def) {
     55     return WrapInNameSpace(def.defined_namespace, NormalizedName(def));
     56   }
     57 
     58   std::string GenTypeName(const Type &type) {
     59     auto bits = NumToString(SizeOf(type.base_type) * 8);
     60     if (IsInteger(type.base_type)) return "int" + bits;
     61     if (IsFloat(type.base_type)) return "float" + bits;
     62     if (type.base_type == BASE_TYPE_STRING) return "string";
     63     if (type.base_type == BASE_TYPE_STRUCT) return "table";
     64     return "none";
     65   }
     66 
     67   std::string LobsterType(const Type &type) {
     68     if (IsFloat(type.base_type)) return "float";
     69     return "int";
     70   }
     71 
     72   // Returns the method name for use with add/put calls.
     73   std::string GenMethod(const Type &type) {
     74     return IsScalar(type.base_type)
     75       ? MakeCamel(GenTypeBasic(type))
     76       : (IsStruct(type) ? "Struct" : "UOffsetTRelative");
     77   }
     78 
     79   // This uses Python names for now..
     80   std::string GenTypeBasic(const Type &type) {
     81     static const char *ctypename[] = {
     82       // clang-format off
     83       #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
     84         CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
     85         #PTYPE,
     86       FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
     87       #undef FLATBUFFERS_TD
     88       // clang-format on
     89     };
     90     return ctypename[type.base_type];
     91   }
     92 
     93   // Generate a struct field, conditioned on its child type(s).
     94   void GenStructAccessor(const StructDef &struct_def,
     95                          const FieldDef &field, std::string *code_ptr) {
     96     GenComment(field.doc_comment, code_ptr, nullptr, "    ");
     97     std::string &code = *code_ptr;
     98     auto offsets = NumToString(field.value.offset);
     99     auto def = "    def " + NormalizedName(field);
    100     if (IsScalar(field.value.type.base_type)) {
    101       if (struct_def.fixed) {
    102         code += def + "():\n        buf_.read_" +
    103                 GenTypeName(field.value.type) + "_le(pos_ + " + offsets +
    104                 ")\n";
    105       } else {
    106         code += def + "():\n        buf_.flatbuffers_field_" +
    107                 GenTypeName(field.value.type) + "(pos_, " + offsets + ", " +
    108                 field.value.constant + ")\n";
    109       }
    110       return;
    111     }
    112     switch (field.value.type.base_type) {
    113       case BASE_TYPE_STRUCT: {
    114         auto name = NamespacedName(*field.value.type.struct_def);
    115         code += def + "():\n        ";
    116         if (struct_def.fixed) {
    117           code += name + "{ buf_, pos_ + " + offsets + " }\n";
    118         } else {
    119           code += std::string("o := buf_.flatbuffers_field_") +
    120                   (field.value.type.struct_def->fixed ? "struct" : "table") +
    121                   "(pos_, " + offsets + ")\n        if o: " + name +
    122                   " { buf_, o } else: nil\n";
    123         }
    124         break;
    125       }
    126       case BASE_TYPE_STRING:
    127         code += def + "():\n        buf_.flatbuffers_field_string(pos_, " +
    128                 offsets + ")\n";
    129         break;
    130       case BASE_TYPE_VECTOR: {
    131         auto vectortype = field.value.type.VectorType();
    132         code += def + "(i:int):\n        ";
    133         if (vectortype.base_type == BASE_TYPE_STRUCT) {
    134           auto start = "buf_.flatbuffers_field_vector(pos_, " + offsets +
    135                        ") + i * " + NumToString(InlineSize(vectortype));
    136           if (!(vectortype.struct_def->fixed)) {
    137             start = "buf_.flatbuffers_indirect(" + start + ")";
    138           }
    139           code += NamespacedName(*field.value.type.struct_def) + " { buf_, " +
    140                   start + " }\n";
    141         } else {
    142           if (vectortype.base_type == BASE_TYPE_STRING)
    143             code += "buf_.flatbuffers_string";
    144           else
    145             code += "buf_.read_" + GenTypeName(vectortype) + "_le";
    146           code += "(buf_.flatbuffers_field_vector(pos_, " + offsets +
    147                   ") + i * " + NumToString(InlineSize(vectortype)) + ")\n";
    148         }
    149         break;
    150       }
    151       case BASE_TYPE_UNION: {
    152         for (auto it = field.value.type.enum_def->vals.vec.begin();
    153              it != field.value.type.enum_def->vals.vec.end(); ++it) {
    154           auto &ev = **it;
    155           if (ev.value) {
    156             code += def + "_as_" + ev.name + "():\n        " +
    157                     NamespacedName(*ev.union_type.struct_def) +
    158                     " { buf_, buf_.flatbuffers_field_table(pos_, " + offsets +
    159                     ") }\n";
    160           }
    161         }
    162         break;
    163       }
    164       default: FLATBUFFERS_ASSERT(0);
    165     }
    166     if (field.value.type.base_type == BASE_TYPE_VECTOR) {
    167       code += def +
    168               "_length():\n        buf_.flatbuffers_field_vector_len(pos_, " +
    169               offsets + ")\n";
    170     }
    171   }
    172 
    173   // Generate table constructors, conditioned on its members' types.
    174   void GenTableBuilders(const StructDef &struct_def,
    175                         std::string *code_ptr) {
    176     std::string &code = *code_ptr;
    177     code += "def " + NormalizedName(struct_def) +
    178             "Start(b_:flatbuffers_builder):\n    b_.StartObject(" +
    179             NumToString(struct_def.fields.vec.size()) + ")\n";
    180     for (auto it = struct_def.fields.vec.begin();
    181         it != struct_def.fields.vec.end(); ++it) {
    182       auto &field = **it;
    183       if (field.deprecated) continue;
    184       auto offset = it - struct_def.fields.vec.begin();
    185       code += "def " + NormalizedName(struct_def) + "Add" +
    186               MakeCamel(NormalizedName(field)) + "(b_:flatbuffers_builder, " +
    187               NormalizedName(field) + ":" + LobsterType(field.value.type) +
    188               "):\n    b_.Prepend" + GenMethod(field.value.type) + "Slot(" +
    189               NumToString(offset) + ", " + NormalizedName(field) + ", " +
    190               field.value.constant + ")\n";
    191       if (field.value.type.base_type == BASE_TYPE_VECTOR) {
    192         code += "def " + NormalizedName(struct_def) + "Start" +
    193                 MakeCamel(NormalizedName(field)) +
    194                 "Vector(b_:flatbuffers_builder, n_:int):\n    b_.StartVector(";
    195         auto vector_type = field.value.type.VectorType();
    196         auto alignment = InlineAlignment(vector_type);
    197         auto elem_size = InlineSize(vector_type);
    198         code += NumToString(elem_size) + ", n_, " + NumToString(alignment) +
    199                 ")\n";
    200         if (vector_type.base_type != BASE_TYPE_STRUCT ||
    201             !vector_type.struct_def->fixed) {
    202           code += "def " + NormalizedName(struct_def) + "Create" +
    203                   MakeCamel(NormalizedName(field)) +
    204                   "Vector(b_:flatbuffers_builder, v_:[" +
    205                   LobsterType(vector_type) + "]):\n    b_.StartVector(" +
    206                   NumToString(elem_size) + ", v_.length, " +
    207                   NumToString(alignment) +
    208                   ")\n    reverse(v_) e_: b_.Prepend" +
    209                   GenMethod(vector_type) +
    210                   "(e_)\n    b_.EndVector(v_.length)\n";
    211         }
    212       }
    213     }
    214     code += "def " + NormalizedName(struct_def) +
    215             "End(b_:flatbuffers_builder):\n    b_.EndObject()\n\n";
    216   }
    217 
    218   void GenStructPreDecl(const StructDef &struct_def, std::string *code_ptr) {
    219     if (struct_def.generated) return;
    220     std::string &code = *code_ptr;
    221     CheckNameSpace(struct_def, &code);
    222     code += "struct " + NormalizedName(struct_def) + "\n\n";
    223   }
    224 
    225   // Generate struct or table methods.
    226   void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
    227     if (struct_def.generated) return;
    228     std::string &code = *code_ptr;
    229     CheckNameSpace(struct_def, &code);
    230     GenComment(struct_def.doc_comment, code_ptr, nullptr, "");
    231     code += "struct " + NormalizedName(struct_def) + " : flatbuffers_handle\n";
    232     for (auto it = struct_def.fields.vec.begin();
    233         it != struct_def.fields.vec.end(); ++it) {
    234       auto &field = **it;
    235       if (field.deprecated) continue;
    236       GenStructAccessor(struct_def, field, code_ptr);
    237     }
    238     code += "\n";
    239     if (!struct_def.fixed) {
    240       // Generate a special accessor for the table that has been declared as
    241       // the root type.
    242       code += "def GetRootAs" + NormalizedName(struct_def) + "(buf:string): " +
    243               NormalizedName(struct_def) +
    244               " { buf, buf.flatbuffers_indirect(0) }\n\n";
    245     }
    246     if (struct_def.fixed) {
    247       // create a struct constructor function
    248       GenStructBuilder(struct_def, code_ptr);
    249     } else {
    250       // Create a set of functions that allow table construction.
    251       GenTableBuilders(struct_def, code_ptr);
    252     }
    253   }
    254 
    255   // Generate enum declarations.
    256   void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
    257     if (enum_def.generated) return;
    258     std::string &code = *code_ptr;
    259     CheckNameSpace(enum_def, &code);
    260     GenComment(enum_def.doc_comment, code_ptr, nullptr, "");
    261     code += "enum + \n";
    262     for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
    263         ++it) {
    264       auto &ev = **it;
    265       GenComment(ev.doc_comment, code_ptr, nullptr, "    ");
    266       code += "    " + enum_def.name + "_" + NormalizedName(ev) + " = " +
    267               NumToString(ev.value);
    268       if (it + 1 != enum_def.vals.vec.end()) code += ",";
    269       code += "\n";
    270     }
    271     code += "\n";
    272   }
    273 
    274   // Recursively generate arguments for a constructor, to deal with nested
    275   // structs.
    276   void StructBuilderArgs(const StructDef &struct_def,
    277                          const char *nameprefix, std::string *code_ptr) {
    278     for (auto it = struct_def.fields.vec.begin();
    279          it != struct_def.fields.vec.end(); ++it) {
    280       auto &field = **it;
    281       if (IsStruct(field.value.type)) {
    282         // Generate arguments for a struct inside a struct. To ensure names
    283         // don't clash, and to make it obvious these arguments are constructing
    284         // a nested struct, prefix the name with the field name.
    285         StructBuilderArgs(*field.value.type.struct_def,
    286           (nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr);
    287       } else {
    288         std::string &code = *code_ptr;
    289         code += ", " + (nameprefix + NormalizedName(field)) + ":" +
    290                 LobsterType(field.value.type);
    291       }
    292     }
    293   }
    294 
    295   // Recursively generate struct construction statements and instert manual
    296   // padding.
    297   void StructBuilderBody(const StructDef &struct_def,
    298                          const char *nameprefix, std::string *code_ptr) {
    299     std::string &code = *code_ptr;
    300     code += "    b_.Prep(" + NumToString(struct_def.minalign) + ", " +
    301             NumToString(struct_def.bytesize) + ")\n";
    302     for (auto it = struct_def.fields.vec.rbegin();
    303          it != struct_def.fields.vec.rend(); ++it) {
    304       auto &field = **it;
    305       if (field.padding)
    306         code += "    b_.Pad(" + NumToString(field.padding) + ")\n";
    307       if (IsStruct(field.value.type)) {
    308         StructBuilderBody(*field.value.type.struct_def,
    309           (nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr);
    310       } else {
    311         code += "    b_.Prepend" + GenMethod(field.value.type) + "(" +
    312                 nameprefix + NormalizedName(field) + ")\n";
    313       }
    314     }
    315   }
    316 
    317   // Create a struct with a builder and the struct's arguments.
    318   void GenStructBuilder(const StructDef &struct_def,
    319                               std::string *code_ptr) {
    320     std::string &code = *code_ptr;
    321     code += "def Create" + NormalizedName(struct_def) +
    322             "(b_:flatbuffers_builder";
    323     StructBuilderArgs(struct_def, "", code_ptr);
    324     code += "):\n";
    325     StructBuilderBody(struct_def, "", code_ptr);
    326     code += "    return b_.Offset()\n\n";
    327   }
    328 
    329   void CheckNameSpace(const Definition &def, std::string *code_ptr) {
    330     auto ns = GetNameSpace(def);
    331     if (ns == current_namespace_) return;
    332     current_namespace_ = ns;
    333     std::string &code = *code_ptr;
    334     code += "namespace " + ns + "\n\n";
    335   }
    336 
    337   bool generate() {
    338     std::string code;
    339     code += std::string("// ") + FlatBuffersGeneratedWarning() +
    340             "\n\ninclude \"flatbuffers.lobster\"\n\n";
    341     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
    342          ++it) {
    343       auto &enum_def = **it;
    344       GenEnum(enum_def, &code);
    345     }
    346     for (auto it = parser_.structs_.vec.begin();
    347          it != parser_.structs_.vec.end(); ++it) {
    348       auto &struct_def = **it;
    349       GenStructPreDecl(struct_def, &code);
    350     }
    351     for (auto it = parser_.structs_.vec.begin();
    352          it != parser_.structs_.vec.end(); ++it) {
    353       auto &struct_def = **it;
    354       GenStruct(struct_def, &code);
    355     }
    356     return SaveFile((path_ + file_name_ + "_generated.lobster").c_str(),
    357                     code, false);
    358   }
    359 
    360  private:
    361   std::unordered_set<std::string> keywords_;
    362   std::string current_namespace_;
    363 };
    364 
    365 }  // namespace lobster
    366 
    367 bool GenerateLobster(const Parser &parser, const std::string &path,
    368                     const std::string &file_name) {
    369   lobster::LobsterGenerator generator(parser, path, file_name);
    370   return generator.generate();
    371 }
    372 
    373 }  // namespace flatbuffers
    374