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