/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 |