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