1 /*
2 * Copyright 2018 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 "idl_gen_lobster.h"
18
19 #include <string>
20 #include <unordered_set>
21
22 #include "flatbuffers/code_generators.h"
23 #include "flatbuffers/flatbuffers.h"
24 #include "flatbuffers/idl.h"
25 #include "flatbuffers/util.h"
26
27 namespace flatbuffers {
28 namespace lobster {
29
30 class LobsterGenerator : public BaseGenerator {
31 public:
LobsterGenerator(const Parser & parser,const std::string & path,const std::string & file_name)32 LobsterGenerator(const Parser &parser, const std::string &path,
33 const std::string &file_name)
34 : BaseGenerator(parser, path, file_name, "" /* not used */, ".",
35 "lobster") {
36 static const char *const keywords[] = {
37 "nil", "true", "false", "return", "struct", "class",
38 "import", "int", "float", "string", "any", "def",
39 "is", "from", "program", "private", "coroutine", "resource",
40 "enum", "typeof", "var", "let", "pakfile", "switch",
41 "case", "default", "namespace", "not", "and", "or",
42 "bool",
43 };
44 keywords_.insert(std::begin(keywords), std::end(keywords));
45 }
46
EscapeKeyword(const std::string & name) const47 std::string EscapeKeyword(const std::string &name) const {
48 return keywords_.find(name) == keywords_.end() ? name : name + "_";
49 }
50
NormalizedName(const Definition & definition) const51 std::string NormalizedName(const Definition &definition) const {
52 return EscapeKeyword(definition.name);
53 }
54
NormalizedName(const EnumVal & ev) const55 std::string NormalizedName(const EnumVal &ev) const {
56 return EscapeKeyword(ev.name);
57 }
58
NamespacedName(const Definition & def)59 std::string NamespacedName(const Definition &def) {
60 return WrapInNameSpace(def.defined_namespace, NormalizedName(def));
61 }
62
GenTypeName(const Type & type)63 std::string GenTypeName(const Type &type) {
64 auto bits = NumToString(SizeOf(type.base_type) * 8);
65 if (IsInteger(type.base_type)) {
66 if (IsUnsigned(type.base_type))
67 return "uint" + bits;
68 else
69 return "int" + bits;
70 }
71 if (IsFloat(type.base_type)) return "float" + bits;
72 if (IsString(type)) return "string";
73 if (type.base_type == BASE_TYPE_STRUCT) return "table";
74 return "none";
75 }
76
LobsterType(const Type & type)77 std::string LobsterType(const Type &type) {
78 if (IsFloat(type.base_type)) return "float";
79 if (IsBool(type.base_type)) return "bool";
80 if (IsScalar(type.base_type) && type.enum_def)
81 return NormalizedName(*type.enum_def);
82 if (!IsScalar(type.base_type)) return "flatbuffers.offset";
83 if (IsString(type)) return "string";
84 return "int";
85 }
86
87 // Returns the method name for use with add/put calls.
GenMethod(const Type & type)88 std::string GenMethod(const Type &type) {
89 return IsScalar(type.base_type)
90 ? ConvertCase(GenTypeBasic(type), Case::kUpperCamel)
91 : (IsStruct(type) ? "Struct" : "UOffsetTRelative");
92 }
93
94 // This uses Python names for now..
GenTypeBasic(const Type & type)95 std::string GenTypeBasic(const Type &type) {
96 // clang-format off
97 static const char *ctypename[] = {
98 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
99 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, ...) \
100 #PTYPE,
101 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
102 #undef FLATBUFFERS_TD
103 };
104 // clang-format on
105 return ctypename[type.base_type];
106 }
107
108 // Generate a struct field, conditioned on its child type(s).
GenStructAccessor(const StructDef & struct_def,const FieldDef & field,std::string * code_ptr)109 void GenStructAccessor(const StructDef &struct_def, const FieldDef &field,
110 std::string *code_ptr) {
111 GenComment(field.doc_comment, code_ptr, nullptr, " ");
112 std::string &code = *code_ptr;
113 auto offsets = NumToString(field.value.offset);
114 auto def = " def " + NormalizedName(field);
115 if (IsScalar(field.value.type.base_type)) {
116 std::string acc;
117 if (struct_def.fixed) {
118 acc = "buf_.read_" + GenTypeName(field.value.type) + "_le(pos_ + " +
119 offsets + ")";
120
121 } else {
122 auto defval = field.IsOptional()
123 ? (IsFloat(field.value.type.base_type) ? "0.0" : "0")
124 : field.value.constant;
125 acc = "flatbuffers.field_" + GenTypeName(field.value.type) +
126 "(buf_, pos_, " + offsets + ", " + defval + ")";
127 if (IsBool(field.value.type.base_type)) acc = "bool(" + acc + ")";
128 }
129 if (field.value.type.enum_def)
130 acc = NormalizedName(*field.value.type.enum_def) + "(" + acc + ")";
131 if (field.IsOptional()) {
132 acc += ", flatbuffers.field_present(buf_, pos_, " + offsets + ")";
133 code += def + "() -> " + LobsterType(field.value.type) +
134 ", bool:\n return " + acc + "\n";
135 } else {
136 code += def + "() -> " + LobsterType(field.value.type) +
137 ":\n return " + acc + "\n";
138 }
139 return;
140 }
141 switch (field.value.type.base_type) {
142 case BASE_TYPE_STRUCT: {
143 auto name = NamespacedName(*field.value.type.struct_def);
144 if (struct_def.fixed) {
145 code += def + "() -> " + name + ":\n ";
146 code += "return " + name + "{ buf_, pos_ + " + offsets + " }\n";
147 } else {
148 code += def + "() -> " + name;
149 if (!field.IsRequired()) code += "?";
150 code += ":\n ";
151 code += std::string("let o = flatbuffers.field_") +
152 (field.value.type.struct_def->fixed ? "struct" : "table") +
153 "(buf_, pos_, " + offsets + ")\n return ";
154 if (field.IsRequired()) {
155 code += name + " { buf_, assert o }\n";
156 } else {
157 code += "if o: " + name + " { buf_, o } else: nil\n";
158 }
159 }
160 break;
161 }
162 case BASE_TYPE_STRING:
163 code += def +
164 "() -> string:\n return "
165 "flatbuffers.field_string(buf_, pos_, " +
166 offsets + ")\n";
167 break;
168 case BASE_TYPE_VECTOR: {
169 auto vectortype = field.value.type.VectorType();
170 if (vectortype.base_type == BASE_TYPE_STRUCT) {
171 auto start = "flatbuffers.field_vector(buf_, pos_, " + offsets +
172 ") + i * " + NumToString(InlineSize(vectortype));
173 if (!(vectortype.struct_def->fixed)) {
174 start = "flatbuffers.indirect(buf_, " + start + ")";
175 }
176 code += def + "(i:int) -> " +
177 NamespacedName(*field.value.type.struct_def) +
178 ":\n return ";
179 code += NamespacedName(*field.value.type.struct_def) + " { buf_, " +
180 start + " }\n";
181 } else {
182 if (IsString(vectortype)) {
183 code += def + "(i:int) -> string:\n return ";
184 code += "flatbuffers.string";
185 } else {
186 code += def + "(i:int) -> " + LobsterType(vectortype) +
187 ":\n return ";
188 code += "read_" + GenTypeName(vectortype) + "_le";
189 }
190 code += "(buf_, buf_.flatbuffers.field_vector(pos_, " + offsets +
191 ") + i * " + NumToString(InlineSize(vectortype)) + ")\n";
192 }
193 break;
194 }
195 case BASE_TYPE_UNION: {
196 for (auto it = field.value.type.enum_def->Vals().begin();
197 it != field.value.type.enum_def->Vals().end(); ++it) {
198 auto &ev = **it;
199 if (ev.IsNonZero()) {
200 code += def + "_as_" + ev.name + "():\n return " +
201 NamespacedName(*ev.union_type.struct_def) +
202 " { buf_, flatbuffers.field_table(buf_, pos_, " + offsets +
203 ") }\n";
204 }
205 }
206 break;
207 }
208 default: FLATBUFFERS_ASSERT(0);
209 }
210 if (IsVector(field.value.type)) {
211 code += def +
212 "_length() -> int:\n return "
213 "flatbuffers.field_vector_len(buf_, pos_, " +
214 offsets + ")\n";
215 }
216 }
217
218 // Generate table constructors, conditioned on its members' types.
GenTableBuilders(const StructDef & struct_def,std::string * code_ptr)219 void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) {
220 std::string &code = *code_ptr;
221 code += "struct " + NormalizedName(struct_def) +
222 "Builder:\n b_:flatbuffers.builder\n";
223 code += " def start():\n b_.StartObject(" +
224 NumToString(struct_def.fields.vec.size()) +
225 ")\n return this\n";
226 for (auto it = struct_def.fields.vec.begin();
227 it != struct_def.fields.vec.end(); ++it) {
228 auto &field = **it;
229 if (field.deprecated) continue;
230 auto offset = it - struct_def.fields.vec.begin();
231 code += " def add_" + NormalizedName(field) + "(" +
232 NormalizedName(field) + ":" + LobsterType(field.value.type) +
233 "):\n b_.Prepend" + GenMethod(field.value.type) + "Slot(" +
234 NumToString(offset) + ", " + NormalizedName(field);
235 if (IsScalar(field.value.type.base_type) && !field.IsOptional())
236 code += ", " + field.value.constant;
237 code += ")\n return this\n";
238 }
239 code += " def end():\n return b_.EndObject()\n\n";
240 for (auto it = struct_def.fields.vec.begin();
241 it != struct_def.fields.vec.end(); ++it) {
242 auto &field = **it;
243 if (field.deprecated) continue;
244 if (IsVector(field.value.type)) {
245 code += "def " + NormalizedName(struct_def) + "Start" +
246 ConvertCase(NormalizedName(field), Case::kUpperCamel) +
247 "Vector(b_:flatbuffers.builder, n_:int):\n b_.StartVector(";
248 auto vector_type = field.value.type.VectorType();
249 auto alignment = InlineAlignment(vector_type);
250 auto elem_size = InlineSize(vector_type);
251 code +=
252 NumToString(elem_size) + ", n_, " + NumToString(alignment) + ")\n";
253 if (vector_type.base_type != BASE_TYPE_STRUCT ||
254 !vector_type.struct_def->fixed) {
255 code += "def " + NormalizedName(struct_def) + "Create" +
256 ConvertCase(NormalizedName(field), Case::kUpperCamel) +
257 "Vector(b_:flatbuffers.builder, v_:[" +
258 LobsterType(vector_type) + "]):\n b_.StartVector(" +
259 NumToString(elem_size) + ", v_.length, " +
260 NumToString(alignment) + ")\n reverse(v_) e_: b_.Prepend" +
261 GenMethod(vector_type) +
262 "(e_)\n return b_.EndVector(v_.length)\n";
263 }
264 code += "\n";
265 }
266 }
267 }
268
GenStructPreDecl(const StructDef & struct_def,std::string * code_ptr)269 void GenStructPreDecl(const StructDef &struct_def, std::string *code_ptr) {
270 if (struct_def.generated) return;
271 std::string &code = *code_ptr;
272 CheckNameSpace(struct_def, &code);
273 code += "class " + NormalizedName(struct_def) + "\n\n";
274 }
275
276 // Generate struct or table methods.
GenStruct(const StructDef & struct_def,std::string * code_ptr)277 void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
278 if (struct_def.generated) return;
279 std::string &code = *code_ptr;
280 CheckNameSpace(struct_def, &code);
281 GenComment(struct_def.doc_comment, code_ptr, nullptr, "");
282 code += "class " + NormalizedName(struct_def) + " : flatbuffers.handle\n";
283 for (auto it = struct_def.fields.vec.begin();
284 it != struct_def.fields.vec.end(); ++it) {
285 auto &field = **it;
286 if (field.deprecated) continue;
287 GenStructAccessor(struct_def, field, code_ptr);
288 }
289 code += "\n";
290 if (!struct_def.fixed) {
291 // Generate a special accessor for the table that has been declared as
292 // the root type.
293 code += "def GetRootAs" + NormalizedName(struct_def) +
294 "(buf:string): return " + NormalizedName(struct_def) +
295 " { buf, flatbuffers.indirect(buf, 0) }\n\n";
296 }
297 if (struct_def.fixed) {
298 // create a struct constructor function
299 GenStructBuilder(struct_def, code_ptr);
300 } else {
301 // Create a set of functions that allow table construction.
302 GenTableBuilders(struct_def, code_ptr);
303 }
304 }
305
306 // Generate enum declarations.
GenEnum(const EnumDef & enum_def,std::string * code_ptr)307 void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
308 if (enum_def.generated) return;
309 std::string &code = *code_ptr;
310 CheckNameSpace(enum_def, &code);
311 GenComment(enum_def.doc_comment, code_ptr, nullptr, "");
312 code += "enum " + NormalizedName(enum_def) + ":\n";
313 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
314 auto &ev = **it;
315 GenComment(ev.doc_comment, code_ptr, nullptr, " ");
316 code += " " + enum_def.name + "_" + NormalizedName(ev) + " = " +
317 enum_def.ToString(ev) + "\n";
318 }
319 code += "\n";
320 }
321
322 // Recursively generate arguments for a constructor, to deal with nested
323 // structs.
StructBuilderArgs(const StructDef & struct_def,const char * nameprefix,std::string * code_ptr)324 void StructBuilderArgs(const StructDef &struct_def, const char *nameprefix,
325 std::string *code_ptr) {
326 for (auto it = struct_def.fields.vec.begin();
327 it != struct_def.fields.vec.end(); ++it) {
328 auto &field = **it;
329 if (IsStruct(field.value.type)) {
330 // Generate arguments for a struct inside a struct. To ensure names
331 // don't clash, and to make it obvious these arguments are constructing
332 // a nested struct, prefix the name with the field name.
333 StructBuilderArgs(*field.value.type.struct_def,
334 (nameprefix + (NormalizedName(field) + "_")).c_str(),
335 code_ptr);
336 } else {
337 std::string &code = *code_ptr;
338 code += ", " + (nameprefix + NormalizedName(field)) + ":" +
339 LobsterType(field.value.type);
340 }
341 }
342 }
343
344 // Recursively generate struct construction statements and instert manual
345 // padding.
StructBuilderBody(const StructDef & struct_def,const char * nameprefix,std::string * code_ptr)346 void StructBuilderBody(const StructDef &struct_def, const char *nameprefix,
347 std::string *code_ptr) {
348 std::string &code = *code_ptr;
349 code += " b_.Prep(" + NumToString(struct_def.minalign) + ", " +
350 NumToString(struct_def.bytesize) + ")\n";
351 for (auto it = struct_def.fields.vec.rbegin();
352 it != struct_def.fields.vec.rend(); ++it) {
353 auto &field = **it;
354 if (field.padding)
355 code += " b_.Pad(" + NumToString(field.padding) + ")\n";
356 if (IsStruct(field.value.type)) {
357 StructBuilderBody(*field.value.type.struct_def,
358 (nameprefix + (NormalizedName(field) + "_")).c_str(),
359 code_ptr);
360 } else {
361 code += " b_.Prepend" + GenMethod(field.value.type) + "(" +
362 nameprefix + NormalizedName(field) + ")\n";
363 }
364 }
365 }
366
367 // Create a struct with a builder and the struct's arguments.
GenStructBuilder(const StructDef & struct_def,std::string * code_ptr)368 void GenStructBuilder(const StructDef &struct_def, std::string *code_ptr) {
369 std::string &code = *code_ptr;
370 code +=
371 "def Create" + NormalizedName(struct_def) + "(b_:flatbuffers.builder";
372 StructBuilderArgs(struct_def, "", code_ptr);
373 code += "):\n";
374 StructBuilderBody(struct_def, "", code_ptr);
375 code += " return b_.Offset()\n\n";
376 }
377
CheckNameSpace(const Definition & def,std::string * code_ptr)378 void CheckNameSpace(const Definition &def, std::string *code_ptr) {
379 auto ns = GetNameSpace(def);
380 if (ns == current_namespace_) return;
381 current_namespace_ = ns;
382 std::string &code = *code_ptr;
383 code += "namespace " + ns + "\n\n";
384 }
385
generate()386 bool generate() {
387 std::string code;
388 code += std::string("// ") + FlatBuffersGeneratedWarning() +
389 "\nimport flatbuffers\n\n";
390 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
391 ++it) {
392 auto &enum_def = **it;
393 GenEnum(enum_def, &code);
394 }
395 for (auto it = parser_.structs_.vec.begin();
396 it != parser_.structs_.vec.end(); ++it) {
397 auto &struct_def = **it;
398 GenStructPreDecl(struct_def, &code);
399 }
400 for (auto it = parser_.structs_.vec.begin();
401 it != parser_.structs_.vec.end(); ++it) {
402 auto &struct_def = **it;
403 GenStruct(struct_def, &code);
404 }
405 return SaveFile(GeneratedFileName(path_, file_name_, parser_.opts).c_str(),
406 code, false);
407 }
408
409 private:
410 std::unordered_set<std::string> keywords_;
411 std::string current_namespace_;
412 };
413
414 } // namespace lobster
415
GenerateLobster(const Parser & parser,const std::string & path,const std::string & file_name)416 static bool GenerateLobster(const Parser &parser, const std::string &path,
417 const std::string &file_name) {
418 lobster::LobsterGenerator generator(parser, path, file_name);
419 return generator.generate();
420 }
421
422 namespace {
423
424 class LobsterCodeGenerator : public CodeGenerator {
425 public:
GenerateCode(const Parser & parser,const std::string & path,const std::string & filename)426 Status GenerateCode(const Parser &parser, const std::string &path,
427 const std::string &filename) override {
428 if (!GenerateLobster(parser, path, filename)) { return Status::ERROR; }
429 return Status::OK;
430 }
431
GenerateCode(const uint8_t *,int64_t,const CodeGenOptions &)432 Status GenerateCode(const uint8_t *, int64_t,
433 const CodeGenOptions &) override {
434 return Status::NOT_IMPLEMENTED;
435 }
436
GenerateMakeRule(const Parser & parser,const std::string & path,const std::string & filename,std::string & output)437 Status GenerateMakeRule(const Parser &parser, const std::string &path,
438 const std::string &filename,
439 std::string &output) override {
440 (void)parser;
441 (void)path;
442 (void)filename;
443 (void)output;
444 return Status::NOT_IMPLEMENTED;
445 }
446
GenerateGrpcCode(const Parser & parser,const std::string & path,const std::string & filename)447 Status GenerateGrpcCode(const Parser &parser, const std::string &path,
448 const std::string &filename) override {
449 (void)parser;
450 (void)path;
451 (void)filename;
452 return Status::NOT_IMPLEMENTED;
453 }
454
GenerateRootFile(const Parser & parser,const std::string & path)455 Status GenerateRootFile(const Parser &parser,
456 const std::string &path) override {
457 (void)parser;
458 (void)path;
459 return Status::NOT_IMPLEMENTED;
460 }
461
IsSchemaOnly() const462 bool IsSchemaOnly() const override { return true; }
463
SupportsBfbsGeneration() const464 bool SupportsBfbsGeneration() const override { return false; }
465
SupportsRootFileGeneration() const466 bool SupportsRootFileGeneration() const override { return false; }
467
Language() const468 IDLOptions::Language Language() const override {
469 return IDLOptions::kLobster;
470 }
471
LanguageName() const472 std::string LanguageName() const override { return "Lobster"; }
473 };
474 } // namespace
475
NewLobsterCodeGenerator()476 std::unique_ptr<CodeGenerator> NewLobsterCodeGenerator() {
477 return std::unique_ptr<LobsterCodeGenerator>(new LobsterCodeGenerator());
478 }
479
480 } // namespace flatbuffers
481