• 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_lua.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 
LuaKeywords()41 std::set<std::string> LuaKeywords() {
42   return { "and",   "break", "do",       "else", "elseif", "end",
43            "false", "for",   "function", "goto", "if",     "in",
44            "local", "nil",   "not",      "or",   "repeat", "return",
45            "then",  "true",  "until",    "while" };
46 }
47 
LuaDefaultConfig()48 Namer::Config LuaDefaultConfig() {
49   return { /*types=*/Case::kUpperCamel,
50            /*constants=*/Case::kUnknown,
51            /*methods=*/Case::kUpperCamel,
52            /*functions=*/Case::kUpperCamel,
53            /*fields=*/Case::kUpperCamel,
54            /*variables=*/Case::kLowerCamel,
55            /*variants=*/Case::kKeep,
56            /*enum_variant_seperator=*/"",
57            /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
58            /*namespaces=*/Case::kKeep,
59            /*namespace_seperator=*/"__",
60            /*object_prefix=*/"",
61            /*object_suffix=*/"",
62            /*keyword_prefix=*/"",
63            /*keyword_suffix=*/"_",
64            /*filenames=*/Case::kKeep,
65            /*directories=*/Case::kKeep,
66            /*output_path=*/"",
67            /*filename_suffix=*/"",
68            /*filename_extension=*/".lua" };
69 }
70 
71 class LuaBfbsGenerator : public BaseBfbsGenerator {
72  public:
LuaBfbsGenerator(const std::string & flatc_version)73   explicit LuaBfbsGenerator(const std::string &flatc_version)
74       : BaseBfbsGenerator(),
75         keywords_(),
76         requires_(),
77         current_obj_(nullptr),
78         current_enum_(nullptr),
79         flatc_version_(flatc_version),
80         namer_(LuaDefaultConfig(), LuaKeywords()) {}
81 
GenerateFromSchema(const r::Schema * schema,const CodeGenOptions & options)82   Status GenerateFromSchema(const r::Schema *schema,
83                             const CodeGenOptions &options)
84       FLATBUFFERS_OVERRIDE {
85     options_ = options;
86     if (!GenerateEnums(schema->enums())) { return ERROR; }
87     if (!GenerateObjects(schema->objects(), schema->root_table())) {
88       return ERROR;
89     }
90     return OK;
91   }
92 
93   using BaseBfbsGenerator::GenerateCode;
94 
GenerateCode(const Parser &,const std::string &,const std::string &)95   Status GenerateCode(const Parser &, const std::string &,
96                       const std::string &) override {
97     return Status::NOT_IMPLEMENTED;
98   }
99 
GenerateMakeRule(const Parser & parser,const std::string & path,const std::string & filename,std::string & output)100   Status GenerateMakeRule(const Parser &parser, const std::string &path,
101                           const std::string &filename,
102                           std::string &output) override {
103     (void)parser;
104     (void)path;
105     (void)filename;
106     (void)output;
107     return Status::NOT_IMPLEMENTED;
108   }
109 
GenerateGrpcCode(const Parser & parser,const std::string & path,const std::string & filename)110   Status GenerateGrpcCode(const Parser &parser, const std::string &path,
111                           const std::string &filename) override {
112     (void)parser;
113     (void)path;
114     (void)filename;
115     return Status::NOT_IMPLEMENTED;
116   }
117 
GenerateRootFile(const Parser & parser,const std::string & path)118   Status GenerateRootFile(const Parser &parser,
119                           const std::string &path) override {
120     (void)parser;
121     (void)path;
122     return Status::NOT_IMPLEMENTED;
123   }
124 
IsSchemaOnly() const125   bool IsSchemaOnly() const override { return true; }
126 
SupportsBfbsGeneration() const127   bool SupportsBfbsGeneration() const override { return true; }
128 
SupportsRootFileGeneration() const129   bool SupportsRootFileGeneration() const override { return false; }
130 
Language() const131   IDLOptions::Language Language() const override { return IDLOptions::kLua; }
132 
LanguageName() const133   std::string LanguageName() const override { return "Lua"; }
134 
SupportedAdvancedFeatures() const135   uint64_t SupportedAdvancedFeatures() const FLATBUFFERS_OVERRIDE {
136     return 0xF;
137   }
138 
139  protected:
GenerateEnums(const flatbuffers::Vector<flatbuffers::Offset<r::Enum>> * enums)140   bool GenerateEnums(
141       const flatbuffers::Vector<flatbuffers::Offset<r::Enum>> *enums) {
142     ForAllEnums(enums, [&](const r::Enum *enum_def) {
143       std::string code;
144 
145       StartCodeBlock(enum_def);
146 
147       std::string ns;
148       const std::string enum_name =
149           namer_.Type(namer_.Denamespace(enum_def, ns));
150 
151       GenerateDocumentation(enum_def->documentation(), "", code);
152       code += "local " + enum_name + " = {\n";
153 
154       ForAllEnumValues(enum_def, [&](const reflection::EnumVal *enum_val) {
155         GenerateDocumentation(enum_val->documentation(), "  ", code);
156         code += "  " + namer_.Variant(enum_val->name()->str()) + " = " +
157                 NumToString(enum_val->value()) + ",\n";
158       });
159       code += "}\n";
160       code += "\n";
161 
162       EmitCodeBlock(code, enum_name, ns, enum_def->declaration_file()->str());
163     });
164     return true;
165   }
166 
GenerateObjects(const flatbuffers::Vector<flatbuffers::Offset<r::Object>> * objects,const r::Object * root_object)167   bool GenerateObjects(
168       const flatbuffers::Vector<flatbuffers::Offset<r::Object>> *objects,
169       const r::Object *root_object) {
170     ForAllObjects(objects, [&](const r::Object *object) {
171       std::string code;
172 
173       StartCodeBlock(object);
174 
175       // Register the main flatbuffers module.
176       RegisterRequires("flatbuffers", "flatbuffers");
177 
178       std::string ns;
179       const std::string object_name =
180           namer_.Type(namer_.Denamespace(object, ns));
181 
182       GenerateDocumentation(object->documentation(), "", code);
183 
184       code += "local " + object_name + " = {}\n";
185       code += "local mt = {}\n";
186       code += "\n";
187       code += "function " + object_name + ".New()\n";
188       code += "  local o = {}\n";
189       code += "  setmetatable(o, {__index = mt})\n";
190       code += "  return o\n";
191       code += "end\n";
192       code += "\n";
193 
194       if (object == root_object) {
195         code += "function " + object_name + ".GetRootAs" + object_name +
196                 "(buf, offset)\n";
197         code += "  if type(buf) == \"string\" then\n";
198         code += "    buf = flatbuffers.binaryArray.New(buf)\n";
199         code += "  end\n";
200         code += "\n";
201         code += "  local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)\n";
202         code += "  local o = " + object_name + ".New()\n";
203         code += "  o:Init(buf, n + offset)\n";
204         code += "  return o\n";
205         code += "end\n";
206         code += "\n";
207       }
208 
209       // Generates a init method that receives a pre-existing accessor object,
210       // so that objects can be reused.
211 
212       code += "function mt:Init(buf, pos)\n";
213       code += "  self.view = flatbuffers.view.New(buf, pos)\n";
214       code += "end\n";
215       code += "\n";
216 
217       // Create all the field accessors.
218       ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
219         // Skip writing deprecated fields altogether.
220         if (field->deprecated()) { return; }
221 
222         const std::string field_name = namer_.Field(*field);
223         const r::BaseType base_type = field->type()->base_type();
224 
225         // Generate some fixed strings so we don't repeat outselves later.
226         const std::string getter_signature =
227             "function mt:" + field_name + "()\n";
228         const std::string offset_prefix = "local o = self.view:Offset(" +
229                                           NumToString(field->offset()) + ")\n";
230         const std::string offset_prefix_2 = "if o ~= 0 then\n";
231 
232         GenerateDocumentation(field->documentation(), "", code);
233 
234         if (IsScalar(base_type)) {
235           code += getter_signature;
236 
237           if (object->is_struct()) {
238             // TODO(derekbailey): it would be nice to modify the view:Get to
239             // just pass in the offset and not have to add it its own
240             // self.view.pos.
241             code += "  return " + GenerateGetter(field->type()) +
242                     "self.view.pos + " + NumToString(field->offset()) + ")\n";
243           } else {
244             // Table accessors
245             code += "  " + offset_prefix;
246             code += "  " + offset_prefix_2;
247 
248             std::string getter =
249                 GenerateGetter(field->type()) + "self.view.pos + o)";
250             if (IsBool(base_type)) { getter = "(" + getter + " ~=0)"; }
251             code += "    return " + getter + "\n";
252             code += "  end\n";
253             code += "  return " + DefaultValue(field) + "\n";
254           }
255           code += "end\n";
256           code += "\n";
257         } else {
258           switch (base_type) {
259             case r::String: {
260               code += getter_signature;
261               code += "  " + offset_prefix;
262               code += "  " + offset_prefix_2;
263               code += "    return " + GenerateGetter(field->type()) +
264                       "self.view.pos + o)\n";
265               code += "  end\n";
266               code += "end\n";
267               code += "\n";
268               break;
269             }
270             case r::Obj: {
271               if (object->is_struct()) {
272                 code += "function mt:" + field_name + "(obj)\n";
273                 code += "  obj:Init(self.view.bytes, self.view.pos + " +
274                         NumToString(field->offset()) + ")\n";
275                 code += "  return obj\n";
276                 code += "end\n";
277                 code += "\n";
278               } else {
279                 code += getter_signature;
280                 code += "  " + offset_prefix;
281                 code += "  " + offset_prefix_2;
282 
283                 const r::Object *field_object = GetObject(field->type());
284                 if (!field_object) {
285                   // TODO(derekbailey): this is an error condition. we
286                   // should report it better.
287                   return;
288                 }
289                 code += "    local x = " +
290                         std::string(
291                             field_object->is_struct()
292                                 ? "self.view.pos + o\n"
293                                 : "self.view:Indirect(self.view.pos + o)\n");
294                 const std::string require_name = RegisterRequires(field);
295                 code += "    local obj = " + require_name + ".New()\n";
296                 code += "    obj:Init(self.view.bytes, x)\n";
297                 code += "    return obj\n";
298                 code += "  end\n";
299                 code += "end\n";
300                 code += "\n";
301               }
302               break;
303             }
304             case r::Union: {
305               code += getter_signature;
306               code += "  " + offset_prefix;
307               code += "  " + offset_prefix_2;
308               code +=
309                   "   local obj = "
310                   "flatbuffers.view.New(flatbuffers.binaryArray.New("
311                   "0), 0)\n";
312               code += "    " + GenerateGetter(field->type()) + "obj, o)\n";
313               code += "    return obj\n";
314               code += "  end\n";
315               code += "end\n";
316               code += "\n";
317               break;
318             }
319             case r::Array:
320             case r::Vector: {
321               const r::BaseType vector_base_type = field->type()->element();
322               int32_t element_size = field->type()->element_size();
323               code += "function mt:" + field_name + "(j)\n";
324               code += "  " + offset_prefix;
325               code += "  " + offset_prefix_2;
326 
327               if (IsStructOrTable(vector_base_type)) {
328                 code += "    local x = self.view:Vector(o)\n";
329                 code +=
330                     "    x = x + ((j-1) * " + NumToString(element_size) + ")\n";
331                 if (IsTable(field->type(), /*use_element=*/true)) {
332                   code += "    x = self.view:Indirect(x)\n";
333                 } else {
334                   // Vector of structs are inline, so we need to query the
335                   // size of the struct.
336                   const reflection::Object *obj =
337                       GetObjectByIndex(field->type()->index());
338                   element_size = obj->bytesize();
339                 }
340 
341                 // Include the referenced type, thus we need to make sure
342                 // we set `use_element` to true.
343                 const std::string require_name =
344                     RegisterRequires(field, /*use_element=*/true);
345                 code += "    local obj = " + require_name + ".New()\n";
346                 code += "    obj:Init(self.view.bytes, x)\n";
347                 code += "    return obj\n";
348               } else {
349                 code += "    local a = self.view:Vector(o)\n";
350                 code += "    return " + GenerateGetter(field->type()) +
351                         "a + ((j-1) * " + NumToString(element_size) + "))\n";
352               }
353               code += "  end\n";
354               // Only generate a default value for those types that are
355               // supported.
356               if (!IsStructOrTable(vector_base_type)) {
357                 code +=
358                     "  return " +
359                     std::string(vector_base_type == r::String ? "''\n" : "0\n");
360               }
361               code += "end\n";
362               code += "\n";
363 
364               // If the vector is composed of single byte values, we
365               // generate a helper function to get it as a byte string in
366               // Lua.
367               if (IsSingleByte(vector_base_type)) {
368                 code += "function mt:" + field_name + "AsString(start, stop)\n";
369                 code += "  return self.view:VectorAsString(" +
370                         NumToString(field->offset()) + ", start, stop)\n";
371                 code += "end\n";
372                 code += "\n";
373               }
374 
375               // We also make a new accessor to query just the length of the
376               // vector.
377               code += "function mt:" + field_name + "Length()\n";
378               code += "  " + offset_prefix;
379               code += "  " + offset_prefix_2;
380               code += "    return self.view:VectorLen(o)\n";
381               code += "  end\n";
382               code += "  return 0\n";
383               code += "end\n";
384               code += "\n";
385               break;
386             }
387             default: {
388               return;
389             }
390           }
391         }
392         return;
393       });
394 
395       // Create all the builders
396       if (object->is_struct()) {
397         code += "function " + object_name + ".Create" + object_name +
398                 "(builder" + GenerateStructBuilderArgs(object) + ")\n";
399         code += AppendStructBuilderBody(object);
400         code += "  return builder:Offset()\n";
401         code += "end\n";
402         code += "\n";
403       } else {
404         // Table builders
405         code += "function " + object_name + ".Start(builder)\n";
406         code += "  builder:StartObject(" +
407                 NumToString(object->fields()->size()) + ")\n";
408         code += "end\n";
409         code += "\n";
410 
411         ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
412           if (field->deprecated()) { return; }
413 
414           const std::string field_name = namer_.Field(*field);
415           const std::string variable_name = namer_.Variable(*field);
416 
417           code += "function " + object_name + ".Add" + field_name +
418                   "(builder, " + variable_name + ")\n";
419           code += "  builder:Prepend" + GenerateMethod(field) + "Slot(" +
420                   NumToString(field->id()) + ", " + variable_name + ", " +
421                   DefaultValue(field) + ")\n";
422           code += "end\n";
423           code += "\n";
424 
425           if (IsVector(field->type()->base_type())) {
426             code += "function " + object_name + ".Start" + field_name +
427                     "Vector(builder, numElems)\n";
428 
429             const int32_t element_size = field->type()->element_size();
430             int32_t alignment = 0;
431             if (IsStruct(field->type(), /*use_element=*/true)) {
432               alignment = GetObjectByIndex(field->type()->index())->minalign();
433             } else {
434               alignment = element_size;
435             }
436 
437             code += "  return builder:StartVector(" +
438                     NumToString(element_size) + ", numElems, " +
439                     NumToString(alignment) + ")\n";
440             code += "end\n";
441             code += "\n";
442           }
443         });
444 
445         code += "function " + object_name + ".End(builder)\n";
446         code += "  return builder:EndObject()\n";
447         code += "end\n";
448         code += "\n";
449       }
450 
451       EmitCodeBlock(code, object_name, ns, object->declaration_file()->str());
452     });
453     return true;
454   }
455 
456  private:
GenerateDocumentation(const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> * documentation,std::string indent,std::string & code) const457   void GenerateDocumentation(
458       const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>
459           *documentation,
460       std::string indent, std::string &code) const {
461     flatbuffers::ForAllDocumentation(
462         documentation, [&](const flatbuffers::String *str) {
463           code += indent + "--" + str->str() + "\n";
464         });
465   }
466 
GenerateStructBuilderArgs(const r::Object * object,std::string prefix="") const467   std::string GenerateStructBuilderArgs(const r::Object *object,
468                                         std::string prefix = "") const {
469     std::string signature;
470     ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
471       if (IsStructOrTable(field->type()->base_type())) {
472         const r::Object *field_object = GetObject(field->type());
473         signature += GenerateStructBuilderArgs(
474             field_object, prefix + namer_.Variable(*field) + "_");
475       } else {
476         signature += ", " + prefix + namer_.Variable(*field);
477       }
478     });
479     return signature;
480   }
481 
AppendStructBuilderBody(const r::Object * object,std::string prefix="") const482   std::string AppendStructBuilderBody(const r::Object *object,
483                                       std::string prefix = "") const {
484     std::string code;
485     code += "  builder:Prep(" + NumToString(object->minalign()) + ", " +
486             NumToString(object->bytesize()) + ")\n";
487 
488     // We need to reverse the order we iterate over, since we build the
489     // buffer backwards.
490     ForAllFields(object, /*reverse=*/true, [&](const r::Field *field) {
491       const int32_t num_padding_bytes = field->padding();
492       if (num_padding_bytes) {
493         code += "  builder:Pad(" + NumToString(num_padding_bytes) + ")\n";
494       }
495       if (IsStructOrTable(field->type()->base_type())) {
496         const r::Object *field_object = GetObject(field->type());
497         code += AppendStructBuilderBody(field_object,
498                                         prefix + namer_.Variable(*field) + "_");
499       } else {
500         code += "  builder:Prepend" + GenerateMethod(field) + "(" + prefix +
501                 namer_.Variable(*field) + ")\n";
502       }
503     });
504 
505     return code;
506   }
507 
GenerateMethod(const r::Field * field) const508   std::string GenerateMethod(const r::Field *field) const {
509     const r::BaseType base_type = field->type()->base_type();
510     if (IsScalar(base_type)) { return namer_.Type(GenerateType(base_type)); }
511     if (IsStructOrTable(base_type)) { return "Struct"; }
512     return "UOffsetTRelative";
513   }
514 
GenerateGetter(const r::Type * type,bool element_type=false) const515   std::string GenerateGetter(const r::Type *type,
516                              bool element_type = false) const {
517     switch (element_type ? type->element() : type->base_type()) {
518       case r::String: return "self.view:String(";
519       case r::Union: return "self.view:Union(";
520       case r::Vector: return GenerateGetter(type, true);
521       default:
522         return "self.view:Get(flatbuffers.N." +
523                namer_.Type(GenerateType(type, element_type)) + ", ";
524     }
525   }
526 
GenerateType(const r::Type * type,bool element_type=false) const527   std::string GenerateType(const r::Type *type,
528                            bool element_type = false) const {
529     const r::BaseType base_type =
530         element_type ? type->element() : type->base_type();
531     if (IsScalar(base_type)) { return GenerateType(base_type); }
532     switch (base_type) {
533       case r::String: return "string";
534       case r::Vector: return GenerateGetter(type, true);
535       case r::Obj: return namer_.Type(namer_.Denamespace(GetObject(type)));
536 
537       default: return "*flatbuffers.Table";
538     }
539   }
540 
GenerateType(const r::BaseType base_type) const541   std::string GenerateType(const r::BaseType base_type) const {
542     // Need to override the default naming to match the Lua runtime libraries.
543     // TODO(derekbailey): make overloads in the runtime libraries to avoid this.
544     switch (base_type) {
545       case r::None: return "uint8";
546       case r::UType: return "uint8";
547       case r::Byte: return "int8";
548       case r::UByte: return "uint8";
549       case r::Short: return "int16";
550       case r::UShort: return "uint16";
551       case r::Int: return "int32";
552       case r::UInt: return "uint32";
553       case r::Long: return "int64";
554       case r::ULong: return "uint64";
555       case r::Float: return "Float32";
556       case r::Double: return "Float64";
557       default: return r::EnumNameBaseType(base_type);
558     }
559   }
560 
DefaultValue(const r::Field * field) const561   std::string DefaultValue(const r::Field *field) const {
562     const r::BaseType base_type = field->type()->base_type();
563     if (IsFloatingPoint(base_type)) {
564       return NumToString(field->default_real());
565     }
566     if (IsBool(base_type)) {
567       return field->default_integer() ? "true" : "false";
568     }
569     if (IsScalar(base_type)) { return NumToString((field->default_integer())); }
570     // represents offsets
571     return "0";
572   }
573 
StartCodeBlock(const reflection::Enum * enum_def)574   void StartCodeBlock(const reflection::Enum *enum_def) {
575     current_enum_ = enum_def;
576     current_obj_ = nullptr;
577     requires_.clear();
578   }
579 
StartCodeBlock(const reflection::Object * object)580   void StartCodeBlock(const reflection::Object *object) {
581     current_obj_ = object;
582     current_enum_ = nullptr;
583     requires_.clear();
584   }
585 
RegisterRequires(const r::Field * field,bool use_element=false)586   std::string RegisterRequires(const r::Field *field,
587                                bool use_element = false) {
588     std::string type_name;
589 
590     const r::BaseType type =
591         use_element ? field->type()->element() : field->type()->base_type();
592 
593     if (IsStructOrTable(type)) {
594       const r::Object *object = GetObjectByIndex(field->type()->index());
595       if (object == current_obj_) { return namer_.Denamespace(object); }
596       type_name = object->name()->str();
597     } else {
598       const r::Enum *enum_def = GetEnumByIndex(field->type()->index());
599       if (enum_def == current_enum_) { return namer_.Denamespace(enum_def); }
600       type_name = enum_def->name()->str();
601     }
602 
603     // Prefix with double __ to avoid name clashing, since these are defined
604     // at the top of the file and have lexical scoping. Replace '.' with '_'
605     // so it can be a legal identifier.
606     std::string name = "__" + type_name;
607     std::replace(name.begin(), name.end(), '.', '_');
608 
609     return RegisterRequires(name, type_name);
610   }
611 
RegisterRequires(const std::string & local_name,const std::string & requires_name)612   std::string RegisterRequires(const std::string &local_name,
613                                const std::string &requires_name) {
614     requires_[local_name] = requires_name;
615     return local_name;
616   }
617 
EmitCodeBlock(const std::string & code_block,const std::string & name,const std::string & ns,const std::string & declaring_file) const618   void EmitCodeBlock(const std::string &code_block, const std::string &name,
619                      const std::string &ns,
620                      const std::string &declaring_file) const {
621     const std::string root_type = schema_->root_table()->name()->str();
622     const std::string root_file =
623         schema_->root_table()->declaration_file()->str();
624     const std::string full_qualified_name = ns.empty() ? name : ns + "." + name;
625 
626     std::string code = "--[[ " + full_qualified_name + "\n\n";
627     code +=
628         "  Automatically generated by the FlatBuffers compiler, do not "
629         "modify.\n";
630     code += "  Or modify. I'm a message, not a cop.\n";
631     code += "\n";
632     code += "  flatc version: " + flatc_version_ + "\n";
633     code += "\n";
634     code += "  Declared by  : " + declaring_file + "\n";
635     code += "  Rooting type : " + root_type + " (" + root_file + ")\n";
636     code += "\n--]]\n\n";
637 
638     if (!requires_.empty()) {
639       for (auto it = requires_.cbegin(); it != requires_.cend(); ++it) {
640         code += "local " + it->first + " = require('" + it->second + "')\n";
641       }
642       code += "\n";
643     }
644 
645     code += code_block;
646     code += "return " + name;
647 
648     // Namespaces are '.' deliminted, so replace it with the path separator.
649     std::string path = ns;
650 
651     if (ns.empty()) {
652       path = ".";
653     } else {
654       std::replace(path.begin(), path.end(), '.', '/');
655     }
656 
657     // TODO(derekbailey): figure out a save file without depending on util.h
658     EnsureDirExists(path);
659     const std::string file_name =
660         options_.output_path + path + "/" + namer_.File(name);
661     SaveFile(file_name.c_str(), code, false);
662   }
663 
664   std::unordered_set<std::string> keywords_;
665   std::map<std::string, std::string> requires_;
666   CodeGenOptions options_;
667 
668   const r::Object *current_obj_;
669   const r::Enum *current_enum_;
670   const std::string flatc_version_;
671   const BfbsNamer namer_;
672 };
673 }  // namespace
674 
NewLuaBfbsGenerator(const std::string & flatc_version)675 std::unique_ptr<CodeGenerator> NewLuaBfbsGenerator(
676     const std::string &flatc_version) {
677   return std::unique_ptr<LuaBfbsGenerator>(new LuaBfbsGenerator(flatc_version));
678 }
679 
680 }  // namespace flatbuffers
681