• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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 "bfbs_gen_nim.h"
18 
19 #include <cstdint>
20 #include <map>
21 #include <memory>
22 #include <string>
23 #include <unordered_set>
24 #include <vector>
25 
26 // Ensure no includes to flatc internals. bfbs_gen.h and generator.h are OK.
27 #include "bfbs_gen.h"
28 #include "bfbs_namer.h"
29 
30 // The intermediate representation schema.
31 #include "flatbuffers/code_generator.h"
32 #include "flatbuffers/reflection.h"
33 #include "flatbuffers/reflection_generated.h"
34 
35 namespace flatbuffers {
36 namespace {
37 
38 // To reduce typing
39 namespace r = ::reflection;
40 
NimKeywords()41 std::set<std::string> NimKeywords() {
42   return {
43     "addr",      "and",     "as",        "asm",      "bind",   "block",
44     "break",     "case",    "cast",      "concept",  "const",  "continue",
45     "converter", "defer",   "discard",   "distinct", "div",    "do",
46     "elif",      "else",    "end",       "enum",     "except", "export",
47     "finally",   "for",     "from",      "func",     "if",     "import",
48     "in",        "include", "interface", "is",       "isnot",  "iterator",
49     "let",       "macro",   "method",    "mixin",    "mod",    "nil",
50     "not",       "notin",   "object",    "of",       "or",     "out",
51     "proc",      "ptr",     "raise",     "ref",      "return", "shl",
52     "shr",       "static",  "template",  "try",      "tuple",  "type",
53     "using",     "var",     "when",      "while",    "xor",    "yield",
54   };
55 }
56 
NimDefaultConfig()57 Namer::Config NimDefaultConfig() {
58   return { /*types=*/Case::kUpperCamel,
59            /*constants=*/Case::kUpperCamel,
60            /*methods=*/Case::kLowerCamel,
61            /*functions=*/Case::kUpperCamel,
62            /*fields=*/Case::kLowerCamel,
63            /*variable=*/Case::kLowerCamel,
64            /*variants=*/Case::kUpperCamel,
65            /*enum_variant_seperator=*/".",
66            /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
67            /*namespaces=*/Case::kKeep,
68            /*namespace_seperator=*/"/",
69            /*object_prefix=*/"",
70            /*object_suffix=*/"T",
71            /*keyword_prefix=*/"",
72            /*keyword_suffix=*/"_",
73            /*filenames=*/Case::kKeep,
74            /*directories=*/Case::kKeep,
75            /*output_path=*/"",
76            /*filename_suffix=*/"",
77            /*filename_extension=*/".nim" };
78 }
79 
80 const std::string Export = "*";
81 const std::set<std::string> builtin_types = {
82   "uint8",   "uint8",  "bool",   "int8",  "uint8",   "int16",
83   "uint16",  "int32",  "uint32", "int64", "uint64",  "float32",
84   "float64", "string", "int",    "uint",  "uoffset", "Builder"
85 };
86 
87 class NimBfbsGenerator : public BaseBfbsGenerator {
88  public:
NimBfbsGenerator(const std::string & flatc_version)89   explicit NimBfbsGenerator(const std::string &flatc_version)
90       : BaseBfbsGenerator(),
91         keywords_(),
92         imports_(),
93         current_obj_(nullptr),
94         current_enum_(nullptr),
95         flatc_version_(flatc_version),
96         namer_(NimDefaultConfig(), NimKeywords()) {}
97 
GenerateFromSchema(const r::Schema * schema,const CodeGenOptions & options)98   Status GenerateFromSchema(const r::Schema *schema,
99                             const CodeGenOptions &options)
100       FLATBUFFERS_OVERRIDE {
101     options_ = options;
102     ForAllEnums(schema->enums(), [&](const r::Enum *enum_def) {
103       StartCodeBlock(enum_def);
104       GenerateEnum(enum_def);
105     });
106     ForAllObjects(schema->objects(), [&](const r::Object *object) {
107       StartCodeBlock(object);
108       GenerateObject(object);
109     });
110     return OK;
111   }
112 
113   using BaseBfbsGenerator::GenerateCode;
114 
GenerateCode(const Parser & parser,const std::string & path,const std::string & filename)115   Status GenerateCode(const Parser &parser, const std::string &path,
116                       const std::string &filename) override {
117     (void)parser;
118     (void)path;
119     (void)filename;
120     return NOT_IMPLEMENTED;
121   }
122 
GenerateMakeRule(const Parser & parser,const std::string & path,const std::string & filename,std::string & output)123   Status GenerateMakeRule(const Parser &parser, const std::string &path,
124                           const std::string &filename,
125                           std::string &output) override {
126     (void)parser;
127     (void)path;
128     (void)filename;
129     (void)output;
130     return NOT_IMPLEMENTED;
131   }
132 
GenerateGrpcCode(const Parser & parser,const std::string & path,const std::string & filename)133   Status GenerateGrpcCode(const Parser &parser, const std::string &path,
134                           const std::string &filename) override {
135     (void)parser;
136     (void)path;
137     (void)filename;
138     return NOT_IMPLEMENTED;
139   }
140 
GenerateRootFile(const Parser & parser,const std::string & path)141   Status GenerateRootFile(const Parser &parser,
142                           const std::string &path) override {
143     (void)parser;
144     (void)path;
145     return NOT_IMPLEMENTED;
146   }
147 
IsSchemaOnly() const148   bool IsSchemaOnly() const override { return true; }
149 
SupportsBfbsGeneration() const150   bool SupportsBfbsGeneration() const override { return true; }
151 
SupportsRootFileGeneration() const152   bool SupportsRootFileGeneration() const override { return false; }
153 
Language() const154   IDLOptions::Language Language() const override { return IDLOptions::kNim; }
155 
LanguageName() const156   std::string LanguageName() const override { return "Nim"; }
157 
SupportedAdvancedFeatures() const158   uint64_t SupportedAdvancedFeatures() const FLATBUFFERS_OVERRIDE {
159     return r::AdvancedArrayFeatures | r::AdvancedUnionFeatures |
160            r::OptionalScalars | r::DefaultVectorsAndStrings;
161   }
162 
163  protected:
GenerateEnum(const r::Enum * enum_def)164   void GenerateEnum(const r::Enum *enum_def) {
165     std::string code;
166 
167     std::string ns;
168     const std::string enum_name = namer_.Type(namer_.Denamespace(enum_def, ns));
169     const std::string enum_type =
170         GenerateTypeBasic(enum_def->underlying_type());
171 
172     GenerateDocumentation(enum_def->documentation(), "", code);
173     code += "type " + enum_name + Export + "{.pure.} = enum\n";
174 
175     ForAllEnumValues(enum_def, [&](const reflection::EnumVal *enum_val) {
176       GenerateDocumentation(enum_val->documentation(), "  ", code);
177       code += "  " + namer_.Variant(enum_val->name()->str()) + " = " +
178               NumToString(enum_val->value()) + "." + enum_type + ",\n";
179     });
180 
181     EmitCodeBlock(code, enum_name, ns, enum_def->declaration_file()->str());
182   }
183 
GenerateObject(const r::Object * object)184   void GenerateObject(const r::Object *object) {
185     // Register the main flatbuffers module.
186     RegisterImports("flatbuffers", "");
187     std::string code;
188 
189     std::string ns;
190     const std::string object_name = namer_.Type(namer_.Denamespace(object, ns));
191 
192     GenerateDocumentation(object->documentation(), "", code);
193     code += "type " + object_name + "* = object of FlatObj\n";
194 
195     // Create all the field accessors.
196     ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
197       // Skip writing deprecated fields altogether.
198       if (field->deprecated()) { return; }
199 
200       const std::string field_name = namer_.Field(*field);
201       const r::BaseType base_type = field->type()->base_type();
202       std::string field_type = GenerateType(field->type());
203 
204       if (field->optional() && !object->is_struct()) {
205         RegisterImports("std/options", "");
206         field_type = "Option[" + field_type + "]";
207       }
208 
209       const std::string offset_prefix =
210           "let o = self.tab.Offset(" + NumToString(field->offset()) + ")\n";
211       const std::string offset_prefix_2 = "if o != 0:\n";
212 
213       if (IsScalar(base_type) || base_type == r::String ||
214           base_type == r::Obj || base_type == r::Union) {
215         GenerateDocumentation(field->documentation(), "", code);
216 
217         std::string getter_signature = "func " + namer_.Method(field_name) +
218                                        "*(self: " + object_name +
219                                        "): " + field_type + " =\n";
220         std::string getter_code;
221         std::string setter_signature =
222             "func `" + namer_.Method(field_name + "=") + "`*(self: var " +
223             object_name + ", n: " + field_type + "): bool =\n";
224         std::string setter_code;
225 
226         if (base_type == r::Obj || base_type == r::Union ||
227             field->type()->index() >= 0) {
228           RegisterImports(object, field);
229         }
230 
231         if (object->is_struct()) {
232           std::string field_getter =
233               GenerateGetter(field->type(), NumToString(field->offset()));
234           getter_code += "  return " + field_getter + "\n";
235 
236           if (IsScalar(base_type)) {
237             setter_code += "  return self.tab.Mutate(self.tab.Pos + " +
238                            NumToString(field->offset()) + ", n)\n";
239           }
240         } else {
241           // Table accessors
242           getter_code += "  " + offset_prefix;
243           getter_code += "  " + offset_prefix_2;
244           std::string field_getter = GenerateGetter(field->type(), "o");
245           if (field->optional()) {
246             field_getter = "some(" + field_getter + ")";
247           }
248           getter_code += "    return " + field_getter + "\n";
249           if (!field->optional()) {
250             getter_code += "  return " + DefaultValue(field) + "\n";
251           }
252 
253           if (IsScalar(base_type)) {
254             setter_code += "  return self.tab.MutateSlot(" +
255                            NumToString(field->offset()) + ", n)\n";
256           }
257         }
258         code += getter_signature + getter_code;
259         if (IsScalar(base_type)) { code += setter_signature + setter_code; }
260       } else if (base_type == r::Array || base_type == r::Vector) {
261         const r::BaseType vector_base_type = field->type()->element();
262         uint32_t element_size = field->type()->element_size();
263 
264         if (vector_base_type == r::Obj || vector_base_type == r::Union ||
265             field->type()->index() >= 0) {
266           RegisterImports(object, field, true);
267         }
268 
269         // Get vector length:
270         code += "func " + namer_.Method(field_name + "Length") +
271                 "*(self: " + object_name + "): int = \n";
272         code += "  " + offset_prefix;
273         code += "  " + offset_prefix_2;
274         code += "    return self.tab.VectorLen(o)\n";
275 
276         // Get single vector field:
277         code += "func " + namer_.Method(field_name) + "*(self: " + object_name +
278                 ", j: int): " + GenerateType(field->type(), true) + " = \n";
279         code += "  " + offset_prefix;
280         code += "  " + offset_prefix_2;
281         code += "    var x = self.tab.Vector(o)\n";
282         code +=
283             "    x += j.uoffset * " + NumToString(element_size) + ".uoffset\n";
284         code += "    return " + GenerateGetter(field->type(), "x", true) + "\n";
285 
286         // Get entire vector:
287         code += "func " + namer_.Method(field_name) + "*(self: " + object_name +
288                 "): " + GenerateType(field->type()) + " = \n";
289         code += "  let len = self." + field_name + "Length\n";
290         code += "  for i in countup(0, len - 1):\n";
291         code += "    result.add(self." + field_name + "(i))\n";
292 
293         (void)IsSingleByte(vector_base_type);  // unnused function warning
294       }
295     });
296 
297     // Create all the builders
298     if (object->is_struct()) {
299       code += "proc " + namer_.Function(object_name + "Create") +
300               "*(self: var Builder";
301       code += GenerateStructBuilderArgs(object);
302       code += "): uoffset =\n";
303       code += AppendStructBuilderBody(object);
304       code += "  return self.Offset()\n";
305     } else {
306       // Table builders
307       code += "proc " + namer_.Function(object_name + "Start") +
308               "*(builder: var Builder) =\n";
309       code += "  builder.StartObject(" + NumToString(object->fields()->size()) +
310               ")\n";
311 
312       ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
313         if (field->deprecated()) { return; }
314 
315         const std::string field_name = namer_.Field(*field);
316         const std::string variable_name = namer_.Variable(*field);
317         const std::string variable_type = GenerateTypeBasic(field->type());
318 
319         code += "proc " + namer_.Function(object_name + "Add" + field_name) +
320                 "*(builder: var Builder, " + variable_name + ": " +
321                 variable_type + ") =\n";
322         code += "  builder.Prepend" + GenerateMethod(field) + "Slot(" +
323                 NumToString(field->id()) + ", " + variable_name + ", default(" +
324                 variable_type + "))\n";
325 
326         if (IsVector(field->type()->base_type())) {
327           code += "proc " +
328                   namer_.Function(object_name + "Start" + field_name) +
329                   "Vector*(builder: var Builder, numElems: uoffset) =\n";
330 
331           const int32_t element_size = field->type()->element_size();
332           int32_t alignment = element_size;
333           if (IsStruct(field->type(), /*use_element=*/true)) {
334             alignment = GetObjectByIndex(field->type()->index())->minalign();
335           }
336 
337           code += "  builder.StartVector(" + NumToString(element_size) +
338                   ", numElems, " + NumToString(alignment) + ")\n";
339         }
340       });
341 
342       code += "proc " + namer_.Function(object_name + "End") +
343               "*(builder: var Builder): uoffset =\n";
344       code += "  return builder.EndObject()\n";
345     }
346     EmitCodeBlock(code, object_name, ns, object->declaration_file()->str());
347   }
348 
349  private:
GenerateDocumentation(const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> * documentation,std::string indent,std::string & code) const350   void GenerateDocumentation(
351       const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>
352           *documentation,
353       std::string indent, std::string &code) const {
354     flatbuffers::ForAllDocumentation(
355         documentation, [&](const flatbuffers::String *str) {
356           code += indent + "# " + str->str() + "\n";
357         });
358   }
359 
GenerateStructBuilderArgs(const r::Object * object,std::string prefix="") const360   std::string GenerateStructBuilderArgs(const r::Object *object,
361                                         std::string prefix = "") const {
362     std::string signature;
363     ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
364       if (IsStructOrTable(field->type()->base_type())) {
365         const r::Object *field_object = GetObject(field->type());
366         signature += GenerateStructBuilderArgs(
367             field_object, prefix + namer_.Variable(*field) + "_");
368       } else {
369         signature += ", " + prefix + namer_.Variable(*field) + ": " +
370                      GenerateType(field->type());
371       }
372     });
373     return signature;
374   }
375 
AppendStructBuilderBody(const r::Object * object,std::string prefix="") const376   std::string AppendStructBuilderBody(const r::Object *object,
377                                       std::string prefix = "") const {
378     std::string code;
379     code += "  self.Prep(" + NumToString(object->minalign()) + ", " +
380             NumToString(object->bytesize()) + ")\n";
381 
382     // We need to reverse the order we iterate over, since we build the
383     // buffer backwards.
384     ForAllFields(object, /*reverse=*/true, [&](const r::Field *field) {
385       const int32_t num_padding_bytes = field->padding();
386       if (num_padding_bytes) {
387         code += "  self.Pad(" + NumToString(num_padding_bytes) + ")\n";
388       }
389       if (IsStructOrTable(field->type()->base_type())) {
390         const r::Object *field_object = GetObject(field->type());
391         code += AppendStructBuilderBody(field_object,
392                                         prefix + namer_.Variable(*field) + "_");
393       } else {
394         code += "  self.Prepend(" + prefix + namer_.Variable(*field) + ")\n";
395       }
396     });
397 
398     return code;
399   }
400 
GenerateMethod(const r::Field * field) const401   std::string GenerateMethod(const r::Field *field) const {
402     const r::BaseType base_type = field->type()->base_type();
403     if (IsStructOrTable(base_type)) { return "Struct"; }
404     return "";
405   }
406 
GenerateGetter(const r::Type * type,const std::string & offsetval,bool element_type=false) const407   std::string GenerateGetter(const r::Type *type, const std::string &offsetval,
408                              bool element_type = false) const {
409     const r::BaseType base_type =
410         element_type ? type->element() : type->base_type();
411     std::string offset = offsetval;
412     if (!element_type) { offset = "self.tab.Pos + " + offset; }
413     switch (base_type) {
414       case r::String: return "self.tab.String(" + offset + ")";
415       case r::Union: return "self.tab.Union(" + offsetval + ")";
416       case r::Obj: {
417         return GenerateType(type, element_type) +
418                "(tab: Vtable(Bytes: self.tab.Bytes, Pos: " + offset + "))";
419       }
420       case r::Vector: return GenerateGetter(type, offsetval, true);
421       default:
422         const r::Enum *type_enum = GetEnum(type, element_type);
423         if (type_enum != nullptr) {
424           return GenerateType(type, element_type) + "(" + "Get[" +
425                  GenerateType(base_type) + "](self.tab, " + offset + ")" + ")";
426         } else {
427           return "Get[" + GenerateType(base_type) + "](self.tab, " + offset +
428                  ")";
429         }
430     }
431   }
432 
Denamespace(const std::string & s,std::string & importns,std::string & ns) const433   std::string Denamespace(const std::string &s, std::string &importns,
434                           std::string &ns) const {
435     if (builtin_types.find(s) != builtin_types.end()) { return s; }
436     std::string type = namer_.Type(namer_.Denamespace(s, ns));
437     importns = ns.empty() ? type : ns + "." + type;
438     std::replace(importns.begin(), importns.end(), '.', '_');
439     return type;
440   }
441 
Denamespace(const std::string & s,std::string & importns) const442   std::string Denamespace(const std::string &s, std::string &importns) const {
443     std::string ns;
444     return Denamespace(s, importns, ns);
445   }
446 
Denamespace(const std::string & s) const447   std::string Denamespace(const std::string &s) const {
448     std::string importns;
449     return Denamespace(s, importns);
450   }
451 
GenerateType(const r::Type * type,bool element_type=false,bool enum_inner=false) const452   std::string GenerateType(const r::Type *type, bool element_type = false,
453                            bool enum_inner = false) const {
454     const r::BaseType base_type =
455         element_type ? type->element() : type->base_type();
456     if (IsScalar(base_type) && !enum_inner) {
457       const r::Enum *type_enum = GetEnum(type, element_type);
458       if (type_enum != nullptr) {
459         std::string importns;
460         std::string type_name = Denamespace(type_enum->name()->str(), importns);
461         return importns + "." + type_name;
462       }
463     }
464     if (IsScalar(base_type)) { return Denamespace(GenerateType(base_type)); }
465     switch (base_type) {
466       case r::String: return "string";
467       case r::Vector: {
468         return "seq[" + GenerateType(type, true) + "]";
469       }
470       case r::Union: return "Vtable";
471       case r::Obj: {
472         const r::Object *type_obj = GetObject(type, element_type);
473         std::string importns;
474         std::string type_name = Denamespace(type_obj->name()->str(), importns);
475         if (type_obj == current_obj_) {
476           return type_name;
477         } else {
478           return importns + "." + type_name;
479         }
480       }
481       default: return "uoffset";
482     }
483   }
484 
GenerateTypeBasic(const r::Type * type,bool element_type=false) const485   std::string GenerateTypeBasic(const r::Type *type,
486                                 bool element_type = false) const {
487     const r::BaseType base_type =
488         element_type ? type->element() : type->base_type();
489     if (IsScalar(base_type)) {
490       return GenerateType(base_type);
491     } else {
492       return "uoffset";
493     }
494   }
495 
GenerateType(const r::BaseType base_type) const496   std::string GenerateType(const r::BaseType base_type) const {
497     switch (base_type) {
498       case r::None: return "uint8";
499       case r::UType: return "uint8";
500       case r::Bool: return "bool";
501       case r::Byte: return "int8";
502       case r::UByte: return "uint8";
503       case r::Short: return "int16";
504       case r::UShort: return "uint16";
505       case r::Int: return "int32";
506       case r::UInt: return "uint32";
507       case r::Long: return "int64";
508       case r::ULong: return "uint64";
509       case r::Float: return "float32";
510       case r::Double: return "float64";
511       case r::String: return "string";
512       default: return r::EnumNameBaseType(base_type);
513     }
514   }
515 
DefaultValue(const r::Field * field) const516   std::string DefaultValue(const r::Field *field) const {
517     const r::BaseType base_type = field->type()->base_type();
518     if (IsFloatingPoint(base_type)) {
519       if (field->default_real() != field->default_real()) {
520         return "NaN";
521       } else if (field->default_real() ==
522                  std::numeric_limits<double>::infinity()) {
523         return "Inf";
524       } else if (field->default_real() ==
525                  -std::numeric_limits<double>::infinity()) {
526         return "-Inf";
527       }
528       return NumToString(field->default_real());
529     }
530     if (IsBool(base_type)) {
531       return field->default_integer() ? "true" : "false";
532     }
533     if (IsScalar(base_type)) {
534       const r::Enum *type_enum = GetEnum(field->type());
535       if (type_enum != nullptr) {
536         return "type(result)(" + NumToString((field->default_integer())) + ")";
537       }
538       return NumToString((field->default_integer()));
539     }
540     if (base_type == r::String) { return "\"\""; }
541     // represents offsets
542     return "0";
543   }
544 
StartCodeBlock(const reflection::Enum * enum_def)545   void StartCodeBlock(const reflection::Enum *enum_def) {
546     current_enum_ = enum_def;
547     current_obj_ = nullptr;
548     imports_.clear();
549   }
550 
StartCodeBlock(const reflection::Object * object)551   void StartCodeBlock(const reflection::Object *object) {
552     current_enum_ = nullptr;
553     current_obj_ = object;
554     imports_.clear();
555   }
556 
StringSplit(const std::string orig_str,const std::string token)557   std::vector<std::string> StringSplit(const std::string orig_str,
558                                        const std::string token) {
559     std::vector<std::string> result;
560     std::string str = orig_str;
561     while (str.size()) {
562       size_t index = str.find(token);
563       if (index != std::string::npos) {
564         result.push_back(str.substr(0, index));
565         str = str.substr(index + token.size());
566         if (str.size() == 0) result.push_back(str);
567       } else {
568         result.push_back(str);
569         str = "";
570       }
571     }
572     return result;
573   }
574 
GetRelativePathFromNamespace(const std::string & relative_to,const std::string & str2)575   std::string GetRelativePathFromNamespace(const std::string &relative_to,
576                                            const std::string &str2) {
577     std::vector<std::string> relative_to_vec = StringSplit(relative_to, ".");
578     std::vector<std::string> str2_vec = StringSplit(str2, ".");
579     while (relative_to_vec.size() > 0 && str2_vec.size() > 0) {
580       if (relative_to_vec[0] == str2_vec[0]) {
581         relative_to_vec.erase(relative_to_vec.begin());
582         str2_vec.erase(str2_vec.begin());
583       } else {
584         break;
585       }
586     }
587     relative_to_vec.pop_back();
588     for (size_t i = 0; i < relative_to_vec.size(); ++i) {
589       str2_vec.insert(str2_vec.begin(), std::string(".."));
590     }
591 
592     std::string new_path;
593     for (size_t i = 0; i < str2_vec.size(); ++i) {
594       new_path += str2_vec[i];
595       if (i != str2_vec.size() - 1) { new_path += "/"; }
596     }
597     return new_path;
598   }
599 
RegisterImports(const r::Object * object,const r::Field * field,bool use_element=false)600   void RegisterImports(const r::Object *object, const r::Field *field,
601                        bool use_element = false) {
602     std::string importns;
603     std::string type_name;
604 
605     const r::BaseType type =
606         use_element ? field->type()->element() : field->type()->base_type();
607 
608     if (IsStructOrTable(type)) {
609       const r::Object *object_def = GetObjectByIndex(field->type()->index());
610       if (object_def == current_obj_) { return; }
611       std::string ns;
612       type_name = Denamespace(object_def->name()->str(), importns, ns);
613       type_name = ns.empty() ? type_name : ns + "." + type_name;
614     } else {
615       const r::Enum *enum_def = GetEnumByIndex(field->type()->index());
616       if (enum_def == current_enum_) { return; }
617       std::string ns;
618       type_name = Denamespace(enum_def->name()->str(), importns, ns);
619       type_name = ns.empty() ? type_name : ns + "." + type_name;
620     }
621 
622     std::string import_path =
623         GetRelativePathFromNamespace(object->name()->str(), type_name);
624     std::replace(type_name.begin(), type_name.end(), '.', '_');
625     RegisterImports(import_path, importns);
626   }
627 
RegisterImports(const std::string & local_name,const std::string & imports_name)628   void RegisterImports(const std::string &local_name,
629                        const std::string &imports_name) {
630     imports_[local_name] = imports_name;
631   }
632 
EmitCodeBlock(const std::string & code_block,const std::string & name,const std::string & ns,const std::string & declaring_file)633   void EmitCodeBlock(const std::string &code_block, const std::string &name,
634                      const std::string &ns, const std::string &declaring_file) {
635     const std::string full_qualified_name = ns.empty() ? name : ns + "." + name;
636 
637     std::string code = "#[ " + full_qualified_name + "\n";
638     code +=
639         "  Automatically generated by the FlatBuffers compiler, do not "
640         "modify.\n";
641     code += "  Or modify. I'm a message, not a cop.\n";
642     code += "\n";
643     code += "  flatc version: " + flatc_version_ + "\n";
644     code += "\n";
645     code += "  Declared by  : " + declaring_file + "\n";
646     if (schema_->root_table() != nullptr) {
647       const std::string root_type = schema_->root_table()->name()->str();
648       const std::string root_file =
649           schema_->root_table()->declaration_file()->str();
650       code += "  Rooting type : " + root_type + " (" + root_file + ")\n";
651     }
652     code += "]#\n\n";
653 
654     if (!imports_.empty()) {
655       for (auto it = imports_.cbegin(); it != imports_.cend(); ++it) {
656         if (it->second.empty()) {
657           code += "import " + it->first + "\n";
658         } else {
659           code += "import " + it->first + " as " + it->second + "\n";
660         }
661       }
662       code += "\n";
663     }
664     code += code_block;
665 
666     // Namespaces are '.' deliminted, so replace it with the path separator.
667     std::string path = ns;
668 
669     if (ns.empty()) {
670       path = ".";
671     } else {
672       std::replace(path.begin(), path.end(), '.', '/');
673     }
674 
675     // TODO(derekbailey): figure out a save file without depending on util.h
676     EnsureDirExists(path);
677     const std::string file_name =
678         options_.output_path + path + "/" + namer_.File(name);
679     SaveFile(file_name.c_str(), code, false);
680   }
681 
682   std::unordered_set<std::string> keywords_;
683   std::map<std::string, std::string> imports_;
684   CodeGenOptions options_;
685 
686   const r::Object *current_obj_;
687   const r::Enum *current_enum_;
688   const std::string flatc_version_;
689   const BfbsNamer namer_;
690 };
691 }  // namespace
692 
NewNimBfbsGenerator(const std::string & flatc_version)693 std::unique_ptr<CodeGenerator> NewNimBfbsGenerator(
694     const std::string &flatc_version) {
695   return std::unique_ptr<NimBfbsGenerator>(new NimBfbsGenerator(flatc_version));
696 }
697 
698 }  // namespace flatbuffers
699