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