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
25namespace flatbuffers {
26namespace lobster {
27
28class LobsterGenerator : public BaseGenerator {
29 public:
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
45 std::string EscapeKeyword(const std::string &name) const {
46 return keywords_.find(name) == keywords_.end() ? name : name + "_";
47 }
48
49 std::string NormalizedName(const Definition &definition) const {
50 return EscapeKeyword(definition.name);
51 }
52
53 std::string NormalizedName(const EnumVal &ev) const {
54 return EscapeKeyword(ev.name);
55 }
56
57 std::string NamespacedName(const Definition &def) {
58 return WrapInNameSpace(def.defined_namespace, NormalizedName(def));
59 }
60
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 (IsString(type)) return "string";
66 if (type.base_type == BASE_TYPE_STRUCT) return "table";
67 return "none";
68 }
69
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.
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..
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).
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 auto defval = field.IsOptional() ? "0" : field.value.constant;
114 acc = "buf_.flatbuffers_field_" + GenTypeName(field.value.type) +
115 "(pos_, " + offsets + ", " + defval + ")";
116 }
117 if (field.value.type.enum_def)
118 acc = NormalizedName(*field.value.type.enum_def) + "(" + acc + ")";
119 if (field.IsOptional())
120 acc += ", buf_.flatbuffers_field_present(pos_, " + offsets + ")";
121 code += def + "():\n return " + acc + "\n";
122 return;
123 }
124 switch (field.value.type.base_type) {
125 case BASE_TYPE_STRUCT: {
126 auto name = NamespacedName(*field.value.type.struct_def);
127 code += def + "():\n ";
128 if (struct_def.fixed) {
129 code += "return " + name + "{ buf_, pos_ + " + offsets + " }\n";
130 } else {
131 code += std::string("let o = buf_.flatbuffers_field_") +
132 (field.value.type.struct_def->fixed ? "struct" : "table") +
133 "(pos_, " + offsets + ")\n return if o: " + name +
134 " { buf_, o } else: nil\n";
135 }
136 break;
137 }
138 case BASE_TYPE_STRING:
139 code += def +
140 "():\n return buf_.flatbuffers_field_string(pos_, " +
141 offsets + ")\n";
142 break;
143 case BASE_TYPE_VECTOR: {
144 auto vectortype = field.value.type.VectorType();
145 code += def + "(i:int):\n return ";
146 if (vectortype.base_type == BASE_TYPE_STRUCT) {
147 auto start = "buf_.flatbuffers_field_vector(pos_, " + offsets +
148 ") + i * " + NumToString(InlineSize(vectortype));
149 if (!(vectortype.struct_def->fixed)) {
150 start = "buf_.flatbuffers_indirect(" + start + ")";
151 }
152 code += NamespacedName(*field.value.type.struct_def) + " { buf_, " +
153 start + " }\n";
154 } else {
155 if (IsString(vectortype))
156 code += "buf_.flatbuffers_string";
157 else
158 code += "buf_.read_" + GenTypeName(vectortype) + "_le";
159 code += "(buf_.flatbuffers_field_vector(pos_, " + offsets +
160 ") + i * " + NumToString(InlineSize(vectortype)) + ")\n";
161 }
162 break;
163 }
164 case BASE_TYPE_UNION: {
165 for (auto it = field.value.type.enum_def->Vals().begin();
166 it != field.value.type.enum_def->Vals().end(); ++it) {
167 auto &ev = **it;
168 if (ev.IsNonZero()) {
169 code += def + "_as_" + ev.name + "():\n return " +
170 NamespacedName(*ev.union_type.struct_def) +
171 " { buf_, buf_.flatbuffers_field_table(pos_, " + offsets +
172 ") }\n";
173 }
174 }
175 break;
176 }
177 default: FLATBUFFERS_ASSERT(0);
178 }
179 if (IsVector(field.value.type)) {
180 code += def +
181 "_length():\n return "
182 "buf_.flatbuffers_field_vector_len(pos_, " +
183 offsets + ")\n";
184 }
185 }
186
187 // Generate table constructors, conditioned on its members' types.
188 void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) {
189 std::string &code = *code_ptr;
190 code += "struct " + NormalizedName(struct_def) +
191 "Builder:\n b_:flatbuffers_builder\n";
192 code += " def start():\n b_.StartObject(" +
193 NumToString(struct_def.fields.vec.size()) +
194 ")\n return this\n";
195 for (auto it = struct_def.fields.vec.begin();
196 it != struct_def.fields.vec.end(); ++it) {
197 auto &field = **it;
198 if (field.deprecated) continue;
199 auto offset = it - struct_def.fields.vec.begin();
200 code += " def add_" + NormalizedName(field) + "(" +
201 NormalizedName(field) + ":" + LobsterType(field.value.type) +
202 "):\n b_.Prepend" + GenMethod(field.value.type) + "Slot(" +
203 NumToString(offset) + ", " + NormalizedName(field);
204 if (IsScalar(field.value.type.base_type) && !field.IsOptional())
205 code += ", " + field.value.constant;
206 code += ")\n return this\n";
207 }
208 code += " def end():\n return b_.EndObject()\n\n";
209 for (auto it = struct_def.fields.vec.begin();
210 it != struct_def.fields.vec.end(); ++it) {
211 auto &field = **it;
212 if (field.deprecated) continue;
213 if (IsVector(field.value.type)) {
214 code += "def " + NormalizedName(struct_def) + "Start" +
215 MakeCamel(NormalizedName(field)) +
216 "Vector(b_:flatbuffers_builder, n_:int):\n b_.StartVector(";
217 auto vector_type = field.value.type.VectorType();
218 auto alignment = InlineAlignment(vector_type);
219 auto elem_size = InlineSize(vector_type);
220 code +=
221 NumToString(elem_size) + ", n_, " + NumToString(alignment) + ")\n";
222 if (vector_type.base_type != BASE_TYPE_STRUCT ||
223 !vector_type.struct_def->fixed) {
224 code += "def " + NormalizedName(struct_def) + "Create" +
225 MakeCamel(NormalizedName(field)) +
226 "Vector(b_:flatbuffers_builder, v_:[" +
227 LobsterType(vector_type) + "]):\n b_.StartVector(" +
228 NumToString(elem_size) + ", v_.length, " +
229 NumToString(alignment) + ")\n reverse(v_) e_: b_.Prepend" +
230 GenMethod(vector_type) +
231 "(e_)\n return b_.EndVector(v_.length)\n";
232 }
233 code += "\n";
234 }
235 }
236 }
237
238 void GenStructPreDecl(const StructDef &struct_def, std::string *code_ptr) {
239 if (struct_def.generated) return;
240 std::string &code = *code_ptr;
241 CheckNameSpace(struct_def, &code);
242 code += "class " + NormalizedName(struct_def) + "\n\n";
243 }
244
245 // Generate struct or table methods.
246 void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
247 if (struct_def.generated) return;
248 std::string &code = *code_ptr;
249 CheckNameSpace(struct_def, &code);
250 GenComment(struct_def.doc_comment, code_ptr, nullptr, "");
251 code += "class " + NormalizedName(struct_def) + " : flatbuffers_handle\n";
252 for (auto it = struct_def.fields.vec.begin();
253 it != struct_def.fields.vec.end(); ++it) {
254 auto &field = **it;
255 if (field.deprecated) continue;
256 GenStructAccessor(struct_def, field, code_ptr);
257 }
258 code += "\n";
259 if (!struct_def.fixed) {
260 // Generate a special accessor for the table that has been declared as
261 // the root type.
262 code += "def GetRootAs" + NormalizedName(struct_def) +
263 "(buf:string): return " + NormalizedName(struct_def) +
264 " { buf, buf.flatbuffers_indirect(0) }\n\n";
265 }
266 if (struct_def.fixed) {
267 // create a struct constructor function
268 GenStructBuilder(struct_def, code_ptr);
269 } else {
270 // Create a set of functions that allow table construction.
271 GenTableBuilders(struct_def, code_ptr);
272 }
273 }
274
275 // Generate enum declarations.
276 void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
277 if (enum_def.generated) return;
278 std::string &code = *code_ptr;
279 CheckNameSpace(enum_def, &code);
280 GenComment(enum_def.doc_comment, code_ptr, nullptr, "");
281 code += "enum " + NormalizedName(enum_def) + ":\n";
282 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
283 auto &ev = **it;
284 GenComment(ev.doc_comment, code_ptr, nullptr, " ");
285 code += " " + enum_def.name + "_" + NormalizedName(ev) + " = " +
286 enum_def.ToString(ev) + "\n";
287 }
288 code += "\n";
289 }
290
291 // Recursively generate arguments for a constructor, to deal with nested
292 // structs.
293 void StructBuilderArgs(const StructDef &struct_def, const char *nameprefix,
294 std::string *code_ptr) {
295 for (auto it = struct_def.fields.vec.begin();
296 it != struct_def.fields.vec.end(); ++it) {
297 auto &field = **it;
298 if (IsStruct(field.value.type)) {
299 // Generate arguments for a struct inside a struct. To ensure names
300 // don't clash, and to make it obvious these arguments are constructing
301 // a nested struct, prefix the name with the field name.
302 StructBuilderArgs(*field.value.type.struct_def,
303 (nameprefix + (NormalizedName(field) + "_")).c_str(),
304 code_ptr);
305 } else {
306 std::string &code = *code_ptr;
307 code += ", " + (nameprefix + NormalizedName(field)) + ":" +
308 LobsterType(field.value.type);
309 }
310 }
311 }
312
313 // Recursively generate struct construction statements and instert manual
314 // padding.
315 void StructBuilderBody(const StructDef &struct_def, const char *nameprefix,
316 std::string *code_ptr) {
317 std::string &code = *code_ptr;
318 code += " b_.Prep(" + NumToString(struct_def.minalign) + ", " +
319 NumToString(struct_def.bytesize) + ")\n";
320 for (auto it = struct_def.fields.vec.rbegin();
321 it != struct_def.fields.vec.rend(); ++it) {
322 auto &field = **it;
323 if (field.padding)
324 code += " b_.Pad(" + NumToString(field.padding) + ")\n";
325 if (IsStruct(field.value.type)) {
326 StructBuilderBody(*field.value.type.struct_def,
327 (nameprefix + (NormalizedName(field) + "_")).c_str(),
328 code_ptr);
329 } else {
330 code += " b_.Prepend" + GenMethod(field.value.type) + "(" +
331 nameprefix + NormalizedName(field) + ")\n";
332 }
333 }
334 }
335
336 // Create a struct with a builder and the struct's arguments.
337 void GenStructBuilder(const StructDef &struct_def, std::string *code_ptr) {
338 std::string &code = *code_ptr;
339 code +=
340 "def Create" + NormalizedName(struct_def) + "(b_:flatbuffers_builder";
341 StructBuilderArgs(struct_def, "", code_ptr);
342 code += "):\n";
343 StructBuilderBody(struct_def, "", code_ptr);
344 code += " return b_.Offset()\n\n";
345 }
346
347 void CheckNameSpace(const Definition &def, std::string *code_ptr) {
348 auto ns = GetNameSpace(def);
349 if (ns == current_namespace_) return;
350 current_namespace_ = ns;
351 std::string &code = *code_ptr;
352 code += "namespace " + ns + "\n\n";
353 }
354
355 bool generate() {
356 std::string code;
357 code += std::string("// ") + FlatBuffersGeneratedWarning() +
358 "\nimport flatbuffers\n\n";
359 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
360 ++it) {
361 auto &enum_def = **it;
362 GenEnum(enum_def, &code);
363 }
364 for (auto it = parser_.structs_.vec.begin();
365 it != parser_.structs_.vec.end(); ++it) {
366 auto &struct_def = **it;
367 GenStructPreDecl(struct_def, &code);
368 }
369 for (auto it = parser_.structs_.vec.begin();
370 it != parser_.structs_.vec.end(); ++it) {
371 auto &struct_def = **it;
372 GenStruct(struct_def, &code);
373 }
374 return SaveFile(GeneratedFileName(path_, file_name_, parser_.opts).c_str(),
375 code, false);
376 }
377
378 private:
379 std::unordered_set<std::string> keywords_;
380 std::string current_namespace_;
381};
382
383} // namespace lobster
384
385bool GenerateLobster(const Parser &parser, const std::string &path,
386 const std::string &file_name) {
387 lobster::LobsterGenerator generator(parser, path, file_name);
388 return generator.generate();
389}
390
391} // namespace flatbuffers
392