• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "idl_gen_lobster.h"
18 
19 #include <string>
20 #include <unordered_set>
21 
22 #include "flatbuffers/code_generators.h"
23 #include "flatbuffers/flatbuffers.h"
24 #include "flatbuffers/idl.h"
25 #include "flatbuffers/util.h"
26 
27 namespace flatbuffers {
28 namespace lobster {
29 
30 class LobsterGenerator : public BaseGenerator {
31  public:
LobsterGenerator(const Parser & parser,const std::string & path,const std::string & file_name)32   LobsterGenerator(const Parser &parser, const std::string &path,
33                    const std::string &file_name)
34       : BaseGenerator(parser, path, file_name, "" /* not used */, ".",
35                       "lobster") {
36     static const char *const keywords[] = {
37       "nil",    "true",    "false",     "return",  "struct",    "class",
38       "import", "int",     "float",     "string",  "any",       "def",
39       "is",     "from",    "program",   "private", "coroutine", "resource",
40       "enum",   "typeof",  "var",       "let",     "pakfile",   "switch",
41       "case",   "default", "namespace", "not",     "and",       "or",
42       "bool",
43     };
44     keywords_.insert(std::begin(keywords), std::end(keywords));
45   }
46 
EscapeKeyword(const std::string & name) const47   std::string EscapeKeyword(const std::string &name) const {
48     return keywords_.find(name) == keywords_.end() ? name : name + "_";
49   }
50 
NormalizedName(const Definition & definition) const51   std::string NormalizedName(const Definition &definition) const {
52     return EscapeKeyword(definition.name);
53   }
54 
NormalizedName(const EnumVal & ev) const55   std::string NormalizedName(const EnumVal &ev) const {
56     return EscapeKeyword(ev.name);
57   }
58 
NamespacedName(const Definition & def)59   std::string NamespacedName(const Definition &def) {
60     return WrapInNameSpace(def.defined_namespace, NormalizedName(def));
61   }
62 
GenTypeName(const Type & type)63   std::string GenTypeName(const Type &type) {
64     auto bits = NumToString(SizeOf(type.base_type) * 8);
65     if (IsInteger(type.base_type)) {
66       if (IsUnsigned(type.base_type))
67         return "uint" + bits;
68       else
69         return "int" + bits;
70     }
71     if (IsFloat(type.base_type)) return "float" + bits;
72     if (IsString(type)) return "string";
73     if (type.base_type == BASE_TYPE_STRUCT) return "table";
74     return "none";
75   }
76 
LobsterType(const Type & type)77   std::string LobsterType(const Type &type) {
78     if (IsFloat(type.base_type)) return "float";
79     if (IsBool(type.base_type)) return "bool";
80     if (IsScalar(type.base_type) && type.enum_def)
81       return NormalizedName(*type.enum_def);
82     if (!IsScalar(type.base_type)) return "flatbuffers.offset";
83     if (IsString(type)) return "string";
84     return "int";
85   }
86 
87   // Returns the method name for use with add/put calls.
GenMethod(const Type & type)88   std::string GenMethod(const Type &type) {
89     return IsScalar(type.base_type)
90                ? ConvertCase(GenTypeBasic(type), Case::kUpperCamel)
91                : (IsStruct(type) ? "Struct" : "UOffsetTRelative");
92   }
93 
94   // This uses Python names for now..
GenTypeBasic(const Type & type)95   std::string GenTypeBasic(const Type &type) {
96     // clang-format off
97     static const char *ctypename[] = {
98       #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
99               CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, ...) \
100         #PTYPE,
101         FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
102       #undef FLATBUFFERS_TD
103     };
104     // clang-format on
105     return ctypename[type.base_type];
106   }
107 
108   // Generate a struct field, conditioned on its child type(s).
GenStructAccessor(const StructDef & struct_def,const FieldDef & field,std::string * code_ptr)109   void GenStructAccessor(const StructDef &struct_def, const FieldDef &field,
110                          std::string *code_ptr) {
111     GenComment(field.doc_comment, code_ptr, nullptr, "    ");
112     std::string &code = *code_ptr;
113     auto offsets = NumToString(field.value.offset);
114     auto def = "    def " + NormalizedName(field);
115     if (IsScalar(field.value.type.base_type)) {
116       std::string acc;
117       if (struct_def.fixed) {
118         acc = "buf_.read_" + GenTypeName(field.value.type) + "_le(pos_ + " +
119               offsets + ")";
120 
121       } else {
122         auto defval = field.IsOptional()
123                           ? (IsFloat(field.value.type.base_type) ? "0.0" : "0")
124                           : field.value.constant;
125         acc = "flatbuffers.field_" + GenTypeName(field.value.type) +
126               "(buf_, pos_, " + offsets + ", " + defval + ")";
127         if (IsBool(field.value.type.base_type)) acc = "bool(" + acc + ")";
128       }
129       if (field.value.type.enum_def)
130         acc = NormalizedName(*field.value.type.enum_def) + "(" + acc + ")";
131       if (field.IsOptional()) {
132         acc += ", flatbuffers.field_present(buf_, pos_, " + offsets + ")";
133         code += def + "() -> " + LobsterType(field.value.type) +
134                 ", bool:\n        return " + acc + "\n";
135       } else {
136         code += def + "() -> " + LobsterType(field.value.type) +
137                 ":\n        return " + acc + "\n";
138       }
139       return;
140     }
141     switch (field.value.type.base_type) {
142       case BASE_TYPE_STRUCT: {
143         auto name = NamespacedName(*field.value.type.struct_def);
144         if (struct_def.fixed) {
145           code += def + "() -> " + name + ":\n        ";
146           code += "return " + name + "{ buf_, pos_ + " + offsets + " }\n";
147         } else {
148           code += def + "() -> " + name;
149           if (!field.IsRequired()) code += "?";
150           code += ":\n        ";
151           code += std::string("let o = flatbuffers.field_") +
152                   (field.value.type.struct_def->fixed ? "struct" : "table") +
153                   "(buf_, pos_, " + offsets + ")\n        return ";
154           if (field.IsRequired()) {
155             code += name + " { buf_, assert o }\n";
156           } else {
157             code += "if o: " + name + " { buf_, o } else: nil\n";
158           }
159         }
160         break;
161       }
162       case BASE_TYPE_STRING:
163         code += def +
164                 "() -> string:\n        return "
165                 "flatbuffers.field_string(buf_, pos_, " +
166                 offsets + ")\n";
167         break;
168       case BASE_TYPE_VECTOR: {
169         auto vectortype = field.value.type.VectorType();
170         if (vectortype.base_type == BASE_TYPE_STRUCT) {
171           auto start = "flatbuffers.field_vector(buf_, pos_, " + offsets +
172                        ") + i * " + NumToString(InlineSize(vectortype));
173           if (!(vectortype.struct_def->fixed)) {
174             start = "flatbuffers.indirect(buf_, " + start + ")";
175           }
176           code += def + "(i:int) -> " +
177                   NamespacedName(*field.value.type.struct_def) +
178                   ":\n        return ";
179           code += NamespacedName(*field.value.type.struct_def) + " { buf_, " +
180                   start + " }\n";
181         } else {
182           if (IsString(vectortype)) {
183             code += def + "(i:int) -> string:\n        return ";
184             code += "flatbuffers.string";
185           } else {
186             code += def + "(i:int) -> " + LobsterType(vectortype) +
187                     ":\n        return ";
188             code += "read_" + GenTypeName(vectortype) + "_le";
189           }
190           code += "(buf_, buf_.flatbuffers.field_vector(pos_, " + offsets +
191                   ") + i * " + NumToString(InlineSize(vectortype)) + ")\n";
192         }
193         break;
194       }
195       case BASE_TYPE_UNION: {
196         for (auto it = field.value.type.enum_def->Vals().begin();
197              it != field.value.type.enum_def->Vals().end(); ++it) {
198           auto &ev = **it;
199           if (ev.IsNonZero()) {
200             code += def + "_as_" + ev.name + "():\n        return " +
201                     NamespacedName(*ev.union_type.struct_def) +
202                     " { buf_, flatbuffers.field_table(buf_, pos_, " + offsets +
203                     ") }\n";
204           }
205         }
206         break;
207       }
208       default: FLATBUFFERS_ASSERT(0);
209     }
210     if (IsVector(field.value.type)) {
211       code += def +
212               "_length() -> int:\n        return "
213               "flatbuffers.field_vector_len(buf_, pos_, " +
214               offsets + ")\n";
215     }
216   }
217 
218   // Generate table constructors, conditioned on its members' types.
GenTableBuilders(const StructDef & struct_def,std::string * code_ptr)219   void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) {
220     std::string &code = *code_ptr;
221     code += "struct " + NormalizedName(struct_def) +
222             "Builder:\n    b_:flatbuffers.builder\n";
223     code += "    def start():\n        b_.StartObject(" +
224             NumToString(struct_def.fields.vec.size()) +
225             ")\n        return this\n";
226     for (auto it = struct_def.fields.vec.begin();
227          it != struct_def.fields.vec.end(); ++it) {
228       auto &field = **it;
229       if (field.deprecated) continue;
230       auto offset = it - struct_def.fields.vec.begin();
231       code += "    def add_" + NormalizedName(field) + "(" +
232               NormalizedName(field) + ":" + LobsterType(field.value.type) +
233               "):\n        b_.Prepend" + GenMethod(field.value.type) + "Slot(" +
234               NumToString(offset) + ", " + NormalizedName(field);
235       if (IsScalar(field.value.type.base_type) && !field.IsOptional())
236         code += ", " + field.value.constant;
237       code += ")\n        return this\n";
238     }
239     code += "    def end():\n        return b_.EndObject()\n\n";
240     for (auto it = struct_def.fields.vec.begin();
241          it != struct_def.fields.vec.end(); ++it) {
242       auto &field = **it;
243       if (field.deprecated) continue;
244       if (IsVector(field.value.type)) {
245         code += "def " + NormalizedName(struct_def) + "Start" +
246                 ConvertCase(NormalizedName(field), Case::kUpperCamel) +
247                 "Vector(b_:flatbuffers.builder, n_:int):\n    b_.StartVector(";
248         auto vector_type = field.value.type.VectorType();
249         auto alignment = InlineAlignment(vector_type);
250         auto elem_size = InlineSize(vector_type);
251         code +=
252             NumToString(elem_size) + ", n_, " + NumToString(alignment) + ")\n";
253         if (vector_type.base_type != BASE_TYPE_STRUCT ||
254             !vector_type.struct_def->fixed) {
255           code += "def " + NormalizedName(struct_def) + "Create" +
256                   ConvertCase(NormalizedName(field), Case::kUpperCamel) +
257                   "Vector(b_:flatbuffers.builder, v_:[" +
258                   LobsterType(vector_type) + "]):\n    b_.StartVector(" +
259                   NumToString(elem_size) + ", v_.length, " +
260                   NumToString(alignment) + ")\n    reverse(v_) e_: b_.Prepend" +
261                   GenMethod(vector_type) +
262                   "(e_)\n    return b_.EndVector(v_.length)\n";
263         }
264         code += "\n";
265       }
266     }
267   }
268 
GenStructPreDecl(const StructDef & struct_def,std::string * code_ptr)269   void GenStructPreDecl(const StructDef &struct_def, std::string *code_ptr) {
270     if (struct_def.generated) return;
271     std::string &code = *code_ptr;
272     CheckNameSpace(struct_def, &code);
273     code += "class " + NormalizedName(struct_def) + "\n\n";
274   }
275 
276   // Generate struct or table methods.
GenStruct(const StructDef & struct_def,std::string * code_ptr)277   void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
278     if (struct_def.generated) return;
279     std::string &code = *code_ptr;
280     CheckNameSpace(struct_def, &code);
281     GenComment(struct_def.doc_comment, code_ptr, nullptr, "");
282     code += "class " + NormalizedName(struct_def) + " : flatbuffers.handle\n";
283     for (auto it = struct_def.fields.vec.begin();
284          it != struct_def.fields.vec.end(); ++it) {
285       auto &field = **it;
286       if (field.deprecated) continue;
287       GenStructAccessor(struct_def, field, code_ptr);
288     }
289     code += "\n";
290     if (!struct_def.fixed) {
291       // Generate a special accessor for the table that has been declared as
292       // the root type.
293       code += "def GetRootAs" + NormalizedName(struct_def) +
294               "(buf:string): return " + NormalizedName(struct_def) +
295               " { buf, flatbuffers.indirect(buf, 0) }\n\n";
296     }
297     if (struct_def.fixed) {
298       // create a struct constructor function
299       GenStructBuilder(struct_def, code_ptr);
300     } else {
301       // Create a set of functions that allow table construction.
302       GenTableBuilders(struct_def, code_ptr);
303     }
304   }
305 
306   // Generate enum declarations.
GenEnum(const EnumDef & enum_def,std::string * code_ptr)307   void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
308     if (enum_def.generated) return;
309     std::string &code = *code_ptr;
310     CheckNameSpace(enum_def, &code);
311     GenComment(enum_def.doc_comment, code_ptr, nullptr, "");
312     code += "enum " + NormalizedName(enum_def) + ":\n";
313     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
314       auto &ev = **it;
315       GenComment(ev.doc_comment, code_ptr, nullptr, "    ");
316       code += "    " + enum_def.name + "_" + NormalizedName(ev) + " = " +
317               enum_def.ToString(ev) + "\n";
318     }
319     code += "\n";
320   }
321 
322   // Recursively generate arguments for a constructor, to deal with nested
323   // structs.
StructBuilderArgs(const StructDef & struct_def,const char * nameprefix,std::string * code_ptr)324   void StructBuilderArgs(const StructDef &struct_def, const char *nameprefix,
325                          std::string *code_ptr) {
326     for (auto it = struct_def.fields.vec.begin();
327          it != struct_def.fields.vec.end(); ++it) {
328       auto &field = **it;
329       if (IsStruct(field.value.type)) {
330         // Generate arguments for a struct inside a struct. To ensure names
331         // don't clash, and to make it obvious these arguments are constructing
332         // a nested struct, prefix the name with the field name.
333         StructBuilderArgs(*field.value.type.struct_def,
334                           (nameprefix + (NormalizedName(field) + "_")).c_str(),
335                           code_ptr);
336       } else {
337         std::string &code = *code_ptr;
338         code += ", " + (nameprefix + NormalizedName(field)) + ":" +
339                 LobsterType(field.value.type);
340       }
341     }
342   }
343 
344   // Recursively generate struct construction statements and instert manual
345   // padding.
StructBuilderBody(const StructDef & struct_def,const char * nameprefix,std::string * code_ptr)346   void StructBuilderBody(const StructDef &struct_def, const char *nameprefix,
347                          std::string *code_ptr) {
348     std::string &code = *code_ptr;
349     code += "    b_.Prep(" + NumToString(struct_def.minalign) + ", " +
350             NumToString(struct_def.bytesize) + ")\n";
351     for (auto it = struct_def.fields.vec.rbegin();
352          it != struct_def.fields.vec.rend(); ++it) {
353       auto &field = **it;
354       if (field.padding)
355         code += "    b_.Pad(" + NumToString(field.padding) + ")\n";
356       if (IsStruct(field.value.type)) {
357         StructBuilderBody(*field.value.type.struct_def,
358                           (nameprefix + (NormalizedName(field) + "_")).c_str(),
359                           code_ptr);
360       } else {
361         code += "    b_.Prepend" + GenMethod(field.value.type) + "(" +
362                 nameprefix + NormalizedName(field) + ")\n";
363       }
364     }
365   }
366 
367   // Create a struct with a builder and the struct's arguments.
GenStructBuilder(const StructDef & struct_def,std::string * code_ptr)368   void GenStructBuilder(const StructDef &struct_def, std::string *code_ptr) {
369     std::string &code = *code_ptr;
370     code +=
371         "def Create" + NormalizedName(struct_def) + "(b_:flatbuffers.builder";
372     StructBuilderArgs(struct_def, "", code_ptr);
373     code += "):\n";
374     StructBuilderBody(struct_def, "", code_ptr);
375     code += "    return b_.Offset()\n\n";
376   }
377 
CheckNameSpace(const Definition & def,std::string * code_ptr)378   void CheckNameSpace(const Definition &def, std::string *code_ptr) {
379     auto ns = GetNameSpace(def);
380     if (ns == current_namespace_) return;
381     current_namespace_ = ns;
382     std::string &code = *code_ptr;
383     code += "namespace " + ns + "\n\n";
384   }
385 
generate()386   bool generate() {
387     std::string code;
388     code += std::string("// ") + FlatBuffersGeneratedWarning() +
389             "\nimport flatbuffers\n\n";
390     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
391          ++it) {
392       auto &enum_def = **it;
393       GenEnum(enum_def, &code);
394     }
395     for (auto it = parser_.structs_.vec.begin();
396          it != parser_.structs_.vec.end(); ++it) {
397       auto &struct_def = **it;
398       GenStructPreDecl(struct_def, &code);
399     }
400     for (auto it = parser_.structs_.vec.begin();
401          it != parser_.structs_.vec.end(); ++it) {
402       auto &struct_def = **it;
403       GenStruct(struct_def, &code);
404     }
405     return SaveFile(GeneratedFileName(path_, file_name_, parser_.opts).c_str(),
406                     code, false);
407   }
408 
409  private:
410   std::unordered_set<std::string> keywords_;
411   std::string current_namespace_;
412 };
413 
414 }  // namespace lobster
415 
GenerateLobster(const Parser & parser,const std::string & path,const std::string & file_name)416 static bool GenerateLobster(const Parser &parser, const std::string &path,
417                             const std::string &file_name) {
418   lobster::LobsterGenerator generator(parser, path, file_name);
419   return generator.generate();
420 }
421 
422 namespace {
423 
424 class LobsterCodeGenerator : public CodeGenerator {
425  public:
GenerateCode(const Parser & parser,const std::string & path,const std::string & filename)426   Status GenerateCode(const Parser &parser, const std::string &path,
427                       const std::string &filename) override {
428     if (!GenerateLobster(parser, path, filename)) { return Status::ERROR; }
429     return Status::OK;
430   }
431 
GenerateCode(const uint8_t *,int64_t,const CodeGenOptions &)432   Status GenerateCode(const uint8_t *, int64_t,
433                       const CodeGenOptions &) override {
434     return Status::NOT_IMPLEMENTED;
435   }
436 
GenerateMakeRule(const Parser & parser,const std::string & path,const std::string & filename,std::string & output)437   Status GenerateMakeRule(const Parser &parser, const std::string &path,
438                           const std::string &filename,
439                           std::string &output) override {
440     (void)parser;
441     (void)path;
442     (void)filename;
443     (void)output;
444     return Status::NOT_IMPLEMENTED;
445   }
446 
GenerateGrpcCode(const Parser & parser,const std::string & path,const std::string & filename)447   Status GenerateGrpcCode(const Parser &parser, const std::string &path,
448                           const std::string &filename) override {
449     (void)parser;
450     (void)path;
451     (void)filename;
452     return Status::NOT_IMPLEMENTED;
453   }
454 
GenerateRootFile(const Parser & parser,const std::string & path)455   Status GenerateRootFile(const Parser &parser,
456                           const std::string &path) override {
457     (void)parser;
458     (void)path;
459     return Status::NOT_IMPLEMENTED;
460   }
461 
IsSchemaOnly() const462   bool IsSchemaOnly() const override { return true; }
463 
SupportsBfbsGeneration() const464   bool SupportsBfbsGeneration() const override { return false; }
465 
SupportsRootFileGeneration() const466   bool SupportsRootFileGeneration() const override { return false; }
467 
Language() const468   IDLOptions::Language Language() const override {
469     return IDLOptions::kLobster;
470   }
471 
LanguageName() const472   std::string LanguageName() const override { return "Lobster"; }
473 };
474 }  // namespace
475 
NewLobsterCodeGenerator()476 std::unique_ptr<CodeGenerator> NewLobsterCodeGenerator() {
477   return std::unique_ptr<LobsterCodeGenerator>(new LobsterCodeGenerator());
478 }
479 
480 }  // namespace flatbuffers
481