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 (IsString(type)) 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         auto defval = field.IsOptional() ? "0" : field.value.constant;
114         acc = "buf_.flatbuffers_field_" + GenTypeName(field.value.type) +
115               "(pos_, " + offsets + ", " + defval + ")";
116       }
117       if (field.value.type.enum_def)
118         acc = NormalizedName(*field.value.type.enum_def) + "(" + acc + ")";
119       if (field.IsOptional())
120         acc += ", buf_.flatbuffers_field_present(pos_, " + offsets + ")";
121       code += def + "():\n        return " + acc + "\n";
122       return;
123     }
124     switch (field.value.type.base_type) {
125       case BASE_TYPE_STRUCT: {
126         auto name = NamespacedName(*field.value.type.struct_def);
127         code += def + "():\n        ";
128         if (struct_def.fixed) {
129           code += "return " + name + "{ buf_, pos_ + " + offsets + " }\n";
130         } else {
131           code += std::string("let o = buf_.flatbuffers_field_") +
132                   (field.value.type.struct_def->fixed ? "struct" : "table") +
133                   "(pos_, " + offsets + ")\n        return if o: " + name +
134                   " { buf_, o } else: nil\n";
135         }
136         break;
137       }
138       case BASE_TYPE_STRING:
139         code += def +
140                 "():\n        return buf_.flatbuffers_field_string(pos_, " +
141                 offsets + ")\n";
142         break;
143       case BASE_TYPE_VECTOR: {
144         auto vectortype = field.value.type.VectorType();
145         code += def + "(i:int):\n        return ";
146         if (vectortype.base_type == BASE_TYPE_STRUCT) {
147           auto start = "buf_.flatbuffers_field_vector(pos_, " + offsets +
148                        ") + i * " + NumToString(InlineSize(vectortype));
149           if (!(vectortype.struct_def->fixed)) {
150             start = "buf_.flatbuffers_indirect(" + start + ")";
151           }
152           code += NamespacedName(*field.value.type.struct_def) + " { buf_, " +
153                   start + " }\n";
154         } else {
155           if (IsString(vectortype))
156             code += "buf_.flatbuffers_string";
157           else
158             code += "buf_.read_" + GenTypeName(vectortype) + "_le";
159           code += "(buf_.flatbuffers_field_vector(pos_, " + offsets +
160                   ") + i * " + NumToString(InlineSize(vectortype)) + ")\n";
161         }
162         break;
163       }
164       case BASE_TYPE_UNION: {
165         for (auto it = field.value.type.enum_def->Vals().begin();
166              it != field.value.type.enum_def->Vals().end(); ++it) {
167           auto &ev = **it;
168           if (ev.IsNonZero()) {
169             code += def + "_as_" + ev.name + "():\n        return " +
170                     NamespacedName(*ev.union_type.struct_def) +
171                     " { buf_, buf_.flatbuffers_field_table(pos_, " + offsets +
172                     ") }\n";
173           }
174         }
175         break;
176       }
177       default: FLATBUFFERS_ASSERT(0);
178     }
179     if (IsVector(field.value.type)) {
180       code += def +
181               "_length():\n        return "
182               "buf_.flatbuffers_field_vector_len(pos_, " +
183               offsets + ")\n";
184     }
185   }
186 
187   // Generate table constructors, conditioned on its members' types.
GenTableBuilders(const StructDef & struct_def,std::string * code_ptr)188   void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) {
189     std::string &code = *code_ptr;
190     code += "struct " + NormalizedName(struct_def) +
191             "Builder:\n    b_:flatbuffers_builder\n";
192     code += "    def start():\n        b_.StartObject(" +
193             NumToString(struct_def.fields.vec.size()) +
194             ")\n        return this\n";
195     for (auto it = struct_def.fields.vec.begin();
196          it != struct_def.fields.vec.end(); ++it) {
197       auto &field = **it;
198       if (field.deprecated) continue;
199       auto offset = it - struct_def.fields.vec.begin();
200       code += "    def add_" + NormalizedName(field) + "(" +
201               NormalizedName(field) + ":" + LobsterType(field.value.type) +
202               "):\n        b_.Prepend" + GenMethod(field.value.type) + "Slot(" +
203               NumToString(offset) + ", " + NormalizedName(field);
204       if (IsScalar(field.value.type.base_type) && !field.IsOptional())
205         code += ", " + field.value.constant;
206       code += ")\n        return this\n";
207     }
208     code += "    def end():\n        return b_.EndObject()\n\n";
209     for (auto it = struct_def.fields.vec.begin();
210          it != struct_def.fields.vec.end(); ++it) {
211       auto &field = **it;
212       if (field.deprecated) continue;
213       if (IsVector(field.value.type)) {
214         code += "def " + NormalizedName(struct_def) + "Start" +
215                 MakeCamel(NormalizedName(field)) +
216                 "Vector(b_:flatbuffers_builder, n_:int):\n    b_.StartVector(";
217         auto vector_type = field.value.type.VectorType();
218         auto alignment = InlineAlignment(vector_type);
219         auto elem_size = InlineSize(vector_type);
220         code +=
221             NumToString(elem_size) + ", n_, " + NumToString(alignment) + ")\n";
222         if (vector_type.base_type != BASE_TYPE_STRUCT ||
223             !vector_type.struct_def->fixed) {
224           code += "def " + NormalizedName(struct_def) + "Create" +
225                   MakeCamel(NormalizedName(field)) +
226                   "Vector(b_:flatbuffers_builder, v_:[" +
227                   LobsterType(vector_type) + "]):\n    b_.StartVector(" +
228                   NumToString(elem_size) + ", v_.length, " +
229                   NumToString(alignment) + ")\n    reverse(v_) e_: b_.Prepend" +
230                   GenMethod(vector_type) +
231                   "(e_)\n    return b_.EndVector(v_.length)\n";
232         }
233         code += "\n";
234       }
235     }
236   }
237 
GenStructPreDecl(const StructDef & struct_def,std::string * code_ptr)238   void GenStructPreDecl(const StructDef &struct_def, std::string *code_ptr) {
239     if (struct_def.generated) return;
240     std::string &code = *code_ptr;
241     CheckNameSpace(struct_def, &code);
242     code += "class " + NormalizedName(struct_def) + "\n\n";
243   }
244 
245   // Generate struct or table methods.
GenStruct(const StructDef & struct_def,std::string * code_ptr)246   void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
247     if (struct_def.generated) return;
248     std::string &code = *code_ptr;
249     CheckNameSpace(struct_def, &code);
250     GenComment(struct_def.doc_comment, code_ptr, nullptr, "");
251     code += "class " + NormalizedName(struct_def) + " : flatbuffers_handle\n";
252     for (auto it = struct_def.fields.vec.begin();
253          it != struct_def.fields.vec.end(); ++it) {
254       auto &field = **it;
255       if (field.deprecated) continue;
256       GenStructAccessor(struct_def, field, code_ptr);
257     }
258     code += "\n";
259     if (!struct_def.fixed) {
260       // Generate a special accessor for the table that has been declared as
261       // the root type.
262       code += "def GetRootAs" + NormalizedName(struct_def) +
263               "(buf:string): return " + NormalizedName(struct_def) +
264               " { buf, buf.flatbuffers_indirect(0) }\n\n";
265     }
266     if (struct_def.fixed) {
267       // create a struct constructor function
268       GenStructBuilder(struct_def, code_ptr);
269     } else {
270       // Create a set of functions that allow table construction.
271       GenTableBuilders(struct_def, code_ptr);
272     }
273   }
274 
275   // Generate enum declarations.
GenEnum(const EnumDef & enum_def,std::string * code_ptr)276   void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
277     if (enum_def.generated) return;
278     std::string &code = *code_ptr;
279     CheckNameSpace(enum_def, &code);
280     GenComment(enum_def.doc_comment, code_ptr, nullptr, "");
281     code += "enum " + NormalizedName(enum_def) + ":\n";
282     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
283       auto &ev = **it;
284       GenComment(ev.doc_comment, code_ptr, nullptr, "    ");
285       code += "    " + enum_def.name + "_" + NormalizedName(ev) + " = " +
286               enum_def.ToString(ev) + "\n";
287     }
288     code += "\n";
289   }
290 
291   // Recursively generate arguments for a constructor, to deal with nested
292   // structs.
StructBuilderArgs(const StructDef & struct_def,const char * nameprefix,std::string * code_ptr)293   void StructBuilderArgs(const StructDef &struct_def, const char *nameprefix,
294                          std::string *code_ptr) {
295     for (auto it = struct_def.fields.vec.begin();
296          it != struct_def.fields.vec.end(); ++it) {
297       auto &field = **it;
298       if (IsStruct(field.value.type)) {
299         // Generate arguments for a struct inside a struct. To ensure names
300         // don't clash, and to make it obvious these arguments are constructing
301         // a nested struct, prefix the name with the field name.
302         StructBuilderArgs(*field.value.type.struct_def,
303                           (nameprefix + (NormalizedName(field) + "_")).c_str(),
304                           code_ptr);
305       } else {
306         std::string &code = *code_ptr;
307         code += ", " + (nameprefix + NormalizedName(field)) + ":" +
308                 LobsterType(field.value.type);
309       }
310     }
311   }
312 
313   // Recursively generate struct construction statements and instert manual
314   // padding.
StructBuilderBody(const StructDef & struct_def,const char * nameprefix,std::string * code_ptr)315   void StructBuilderBody(const StructDef &struct_def, const char *nameprefix,
316                          std::string *code_ptr) {
317     std::string &code = *code_ptr;
318     code += "    b_.Prep(" + NumToString(struct_def.minalign) + ", " +
319             NumToString(struct_def.bytesize) + ")\n";
320     for (auto it = struct_def.fields.vec.rbegin();
321          it != struct_def.fields.vec.rend(); ++it) {
322       auto &field = **it;
323       if (field.padding)
324         code += "    b_.Pad(" + NumToString(field.padding) + ")\n";
325       if (IsStruct(field.value.type)) {
326         StructBuilderBody(*field.value.type.struct_def,
327                           (nameprefix + (NormalizedName(field) + "_")).c_str(),
328                           code_ptr);
329       } else {
330         code += "    b_.Prepend" + GenMethod(field.value.type) + "(" +
331                 nameprefix + NormalizedName(field) + ")\n";
332       }
333     }
334   }
335 
336   // Create a struct with a builder and the struct's arguments.
GenStructBuilder(const StructDef & struct_def,std::string * code_ptr)337   void GenStructBuilder(const StructDef &struct_def, std::string *code_ptr) {
338     std::string &code = *code_ptr;
339     code +=
340         "def Create" + NormalizedName(struct_def) + "(b_:flatbuffers_builder";
341     StructBuilderArgs(struct_def, "", code_ptr);
342     code += "):\n";
343     StructBuilderBody(struct_def, "", code_ptr);
344     code += "    return b_.Offset()\n\n";
345   }
346 
CheckNameSpace(const Definition & def,std::string * code_ptr)347   void CheckNameSpace(const Definition &def, std::string *code_ptr) {
348     auto ns = GetNameSpace(def);
349     if (ns == current_namespace_) return;
350     current_namespace_ = ns;
351     std::string &code = *code_ptr;
352     code += "namespace " + ns + "\n\n";
353   }
354 
generate()355   bool generate() {
356     std::string code;
357     code += std::string("// ") + FlatBuffersGeneratedWarning() +
358             "\nimport flatbuffers\n\n";
359     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
360          ++it) {
361       auto &enum_def = **it;
362       GenEnum(enum_def, &code);
363     }
364     for (auto it = parser_.structs_.vec.begin();
365          it != parser_.structs_.vec.end(); ++it) {
366       auto &struct_def = **it;
367       GenStructPreDecl(struct_def, &code);
368     }
369     for (auto it = parser_.structs_.vec.begin();
370          it != parser_.structs_.vec.end(); ++it) {
371       auto &struct_def = **it;
372       GenStruct(struct_def, &code);
373     }
374     return SaveFile(GeneratedFileName(path_, file_name_, parser_.opts).c_str(),
375                     code, false);
376   }
377 
378  private:
379   std::unordered_set<std::string> keywords_;
380   std::string current_namespace_;
381 };
382 
383 }  // namespace lobster
384 
GenerateLobster(const Parser & parser,const std::string & path,const std::string & file_name)385 bool GenerateLobster(const Parser &parser, const std::string &path,
386                      const std::string &file_name) {
387   lobster::LobsterGenerator generator(parser, path, file_name);
388   return generator.generate();
389 }
390 
391 }  // namespace flatbuffers
392