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