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: 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 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 return "int"; 70 } 71 72 // Returns the method name for use with add/put calls. 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.. 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). 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. 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 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. 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. 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. 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. 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. 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 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 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 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