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