Coverage Report

Created: 2026-02-14 07:06

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