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