Coverage Report

Created: 2026-03-27 06:46

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