idl_parser.cpp revision 5f091c46ce04c004e4a2dff3965a24e22025850a
1/* 2 * Copyright 2014 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 <algorithm> 18#include <list> 19 20#include "flatbuffers/flatbuffers.h" 21#include "flatbuffers/hash.h" 22#include "flatbuffers/idl.h" 23#include "flatbuffers/util.h" 24 25namespace flatbuffers { 26 27const char *const kTypeNames[] = { 28 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ 29 IDLTYPE, 30 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) 31 #undef FLATBUFFERS_TD 32 nullptr 33}; 34 35const char kTypeSizes[] = { 36 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ 37 sizeof(CTYPE), 38 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) 39 #undef FLATBUFFERS_TD 40}; 41 42static void Error(const std::string &msg) { 43 throw msg; 44} 45 46// Ensure that integer values we parse fit inside the declared integer type. 47static void CheckBitsFit(int64_t val, size_t bits) { 48 auto mask = (1ll << bits) - 1; // Bits we allow to be used. 49 if (bits < 64 && 50 (val & ~mask) != 0 && // Positive or unsigned. 51 (val | mask) != -1) // Negative. 52 Error("constant does not fit in a " + NumToString(bits) + "-bit field"); 53} 54 55// atot: templated version of atoi/atof: convert a string to an instance of T. 56template<typename T> inline T atot(const char *s) { 57 auto val = StringToInt(s); 58 CheckBitsFit(val, sizeof(T) * 8); 59 return (T)val; 60} 61template<> inline bool atot<bool>(const char *s) { 62 return 0 != atoi(s); 63} 64template<> inline float atot<float>(const char *s) { 65 return static_cast<float>(strtod(s, nullptr)); 66} 67template<> inline double atot<double>(const char *s) { 68 return strtod(s, nullptr); 69} 70 71template<> inline Offset<void> atot<Offset<void>>(const char *s) { 72 return Offset<void>(atoi(s)); 73} 74 75// Declare tokens we'll use. Single character tokens are represented by their 76// ascii character code (e.g. '{'), others above 256. 77#define FLATBUFFERS_GEN_TOKENS(TD) \ 78 TD(Eof, 256, "end of file") \ 79 TD(StringConstant, 257, "string constant") \ 80 TD(IntegerConstant, 258, "integer constant") \ 81 TD(FloatConstant, 259, "float constant") \ 82 TD(Identifier, 260, "identifier") \ 83 TD(Table, 261, "table") \ 84 TD(Struct, 262, "struct") \ 85 TD(Enum, 263, "enum") \ 86 TD(Union, 264, "union") \ 87 TD(NameSpace, 265, "namespace") \ 88 TD(RootType, 266, "root_type") \ 89 TD(FileIdentifier, 267, "file_identifier") \ 90 TD(FileExtension, 268, "file_extension") \ 91 TD(Include, 269, "include") \ 92 TD(Attribute, 270, "attribute") 93#ifdef __GNUC__ 94__extension__ // Stop GCC complaining about trailing comma with -Wpendantic. 95#endif 96enum { 97 #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) kToken ## NAME = VALUE, 98 FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN) 99 #undef FLATBUFFERS_TOKEN 100 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ 101 kToken ## ENUM, 102 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) 103 #undef FLATBUFFERS_TD 104}; 105 106static std::string TokenToString(int t) { 107 static const char *tokens[] = { 108 #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) STRING, 109 FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN) 110 #undef FLATBUFFERS_TOKEN 111 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ 112 IDLTYPE, 113 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) 114 #undef FLATBUFFERS_TD 115 }; 116 if (t < 256) { // A single ascii char token. 117 std::string s; 118 s.append(1, static_cast<char>(t)); 119 return s; 120 } else { // Other tokens. 121 return tokens[t - 256]; 122 } 123} 124 125// Parses exactly nibbles worth of hex digits into a number, or error. 126int64_t Parser::ParseHexNum(int nibbles) { 127 for (int i = 0; i < nibbles; i++) 128 if (!isxdigit(cursor_[i])) 129 Error("escape code must be followed by " + NumToString(nibbles) + 130 " hex digits"); 131 std::string target(cursor_, cursor_ + nibbles); 132 auto val = StringToInt(target.c_str(), 16); 133 cursor_ += nibbles; 134 return val; 135} 136 137void Parser::Next() { 138 doc_comment_.clear(); 139 bool seen_newline = false; 140 for (;;) { 141 char c = *cursor_++; 142 token_ = c; 143 switch (c) { 144 case '\0': cursor_--; token_ = kTokenEof; return; 145 case ' ': case '\r': case '\t': break; 146 case '\n': line_++; seen_newline = true; break; 147 case '{': case '}': case '(': case ')': case '[': case ']': return; 148 case ',': case ':': case ';': case '=': return; 149 case '.': 150 if(!isdigit(*cursor_)) return; 151 Error("floating point constant can\'t start with \".\""); 152 break; 153 case '\"': 154 attribute_ = ""; 155 while (*cursor_ != '\"') { 156 if (*cursor_ < ' ' && *cursor_ >= 0) 157 Error("illegal character in string constant"); 158 if (*cursor_ == '\\') { 159 cursor_++; 160 switch (*cursor_) { 161 case 'n': attribute_ += '\n'; cursor_++; break; 162 case 't': attribute_ += '\t'; cursor_++; break; 163 case 'r': attribute_ += '\r'; cursor_++; break; 164 case 'b': attribute_ += '\b'; cursor_++; break; 165 case 'f': attribute_ += '\f'; cursor_++; break; 166 case '\"': attribute_ += '\"'; cursor_++; break; 167 case '\\': attribute_ += '\\'; cursor_++; break; 168 case '/': attribute_ += '/'; cursor_++; break; 169 case 'x': { // Not in the JSON standard 170 cursor_++; 171 attribute_ += static_cast<char>(ParseHexNum(2)); 172 break; 173 } 174 case 'u': { 175 cursor_++; 176 ToUTF8(static_cast<int>(ParseHexNum(4)), &attribute_); 177 break; 178 } 179 default: Error("unknown escape code in string constant"); break; 180 } 181 } else { // printable chars + UTF-8 bytes 182 attribute_ += *cursor_++; 183 } 184 } 185 cursor_++; 186 token_ = kTokenStringConstant; 187 return; 188 case '/': 189 if (*cursor_ == '/') { 190 const char *start = ++cursor_; 191 while (*cursor_ && *cursor_ != '\n' && *cursor_ != '\r') cursor_++; 192 if (*start == '/') { // documentation comment 193 if (cursor_ != source_ && !seen_newline) 194 Error("a documentation comment should be on a line on its own"); 195 doc_comment_.push_back(std::string(start + 1, cursor_)); 196 } 197 break; 198 } 199 // fall thru 200 default: 201 if (isalpha(static_cast<unsigned char>(c)) || c == '_') { 202 // Collect all chars of an identifier: 203 const char *start = cursor_ - 1; 204 while (isalnum(static_cast<unsigned char>(*cursor_)) || 205 *cursor_ == '_') 206 cursor_++; 207 attribute_.clear(); 208 attribute_.append(start, cursor_); 209 // First, see if it is a type keyword from the table of types: 210 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \ 211 PTYPE) \ 212 if (attribute_ == IDLTYPE) { \ 213 token_ = kToken ## ENUM; \ 214 return; \ 215 } 216 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) 217 #undef FLATBUFFERS_TD 218 // If it's a boolean constant keyword, turn those into integers, 219 // which simplifies our logic downstream. 220 if (attribute_ == "true" || attribute_ == "false") { 221 attribute_ = NumToString(attribute_ == "true"); 222 token_ = kTokenIntegerConstant; 223 return; 224 } 225 // Check for declaration keywords: 226 if (attribute_ == "table") { token_ = kTokenTable; return; } 227 if (attribute_ == "struct") { token_ = kTokenStruct; return; } 228 if (attribute_ == "enum") { token_ = kTokenEnum; return; } 229 if (attribute_ == "union") { token_ = kTokenUnion; return; } 230 if (attribute_ == "namespace") { token_ = kTokenNameSpace; return; } 231 if (attribute_ == "root_type") { token_ = kTokenRootType; return; } 232 if (attribute_ == "include") { token_ = kTokenInclude; return; } 233 if (attribute_ == "attribute") { token_ = kTokenAttribute; return; } 234 if (attribute_ == "file_identifier") { 235 token_ = kTokenFileIdentifier; 236 return; 237 } 238 if (attribute_ == "file_extension") { 239 token_ = kTokenFileExtension; 240 return; 241 } 242 // If not, it is a user-defined identifier: 243 token_ = kTokenIdentifier; 244 return; 245 } else if (isdigit(static_cast<unsigned char>(c)) || c == '-') { 246 const char *start = cursor_ - 1; 247 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++; 248 if (*cursor_ == '.') { 249 cursor_++; 250 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++; 251 // See if this float has a scientific notation suffix. Both JSON 252 // and C++ (through strtod() we use) have the same format: 253 if (*cursor_ == 'e' || *cursor_ == 'E') { 254 cursor_++; 255 if (*cursor_ == '+' || *cursor_ == '-') cursor_++; 256 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++; 257 } 258 token_ = kTokenFloatConstant; 259 } else { 260 token_ = kTokenIntegerConstant; 261 } 262 attribute_.clear(); 263 attribute_.append(start, cursor_); 264 return; 265 } 266 std::string ch; 267 ch = c; 268 if (c < ' ' || c > '~') ch = "code: " + NumToString(c); 269 Error("illegal character: " + ch); 270 break; 271 } 272 } 273} 274 275// Check if a given token is next, if so, consume it as well. 276bool Parser::IsNext(int t) { 277 bool isnext = t == token_; 278 if (isnext) Next(); 279 return isnext; 280} 281 282// Expect a given token to be next, consume it, or error if not present. 283void Parser::Expect(int t) { 284 if (t != token_) { 285 Error("expecting: " + TokenToString(t) + " instead got: " + 286 TokenToString(token_)); 287 } 288 Next(); 289} 290 291void Parser::ParseNamespacing(std::string *id, std::string *last) { 292 while (IsNext('.')) { 293 *id += "."; 294 *id += attribute_; 295 if (last) *last = attribute_; 296 Expect(kTokenIdentifier); 297 } 298} 299 300EnumDef *Parser::LookupEnum(const std::string &id) { 301 auto ed = enums_.Lookup(GetFullyQualifiedName(id)); 302 // id may simply not have a namespace at all, so check that too. 303 if (!ed) ed = enums_.Lookup(id); 304 return ed; 305} 306 307void Parser::ParseTypeIdent(Type &type) { 308 std::string id = attribute_; 309 Expect(kTokenIdentifier); 310 ParseNamespacing(&id, nullptr); 311 auto enum_def = LookupEnum(id); 312 if (enum_def) { 313 type = enum_def->underlying_type; 314 if (enum_def->is_union) type.base_type = BASE_TYPE_UNION; 315 } else { 316 type.base_type = BASE_TYPE_STRUCT; 317 type.struct_def = LookupCreateStruct(id); 318 } 319} 320 321// Parse any IDL type. 322void Parser::ParseType(Type &type) { 323 if (token_ >= kTokenBOOL && token_ <= kTokenSTRING) { 324 type.base_type = static_cast<BaseType>(token_ - kTokenNONE); 325 Next(); 326 } else { 327 if (token_ == kTokenIdentifier) { 328 ParseTypeIdent(type); 329 } else if (token_ == '[') { 330 Next(); 331 Type subtype; 332 ParseType(subtype); 333 if (subtype.base_type == BASE_TYPE_VECTOR) { 334 // We could support this, but it will complicate things, and it's 335 // easier to work around with a struct around the inner vector. 336 Error("nested vector types not supported (wrap in table first)."); 337 } 338 if (subtype.base_type == BASE_TYPE_UNION) { 339 // We could support this if we stored a struct of 2 elements per 340 // union element. 341 Error("vector of union types not supported (wrap in table first)."); 342 } 343 type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def); 344 type.element = subtype.base_type; 345 Expect(']'); 346 } else { 347 Error("illegal type syntax"); 348 } 349 } 350} 351 352FieldDef &Parser::AddField(StructDef &struct_def, 353 const std::string &name, 354 const Type &type) { 355 auto &field = *new FieldDef(); 356 field.value.offset = 357 FieldIndexToOffset(static_cast<voffset_t>(struct_def.fields.vec.size())); 358 field.name = name; 359 field.file = struct_def.file; 360 field.value.type = type; 361 if (struct_def.fixed) { // statically compute the field offset 362 auto size = InlineSize(type); 363 auto alignment = InlineAlignment(type); 364 // structs_ need to have a predictable format, so we need to align to 365 // the largest scalar 366 struct_def.minalign = std::max(struct_def.minalign, alignment); 367 struct_def.PadLastField(alignment); 368 field.value.offset = static_cast<voffset_t>(struct_def.bytesize); 369 struct_def.bytesize += size; 370 } 371 if (struct_def.fields.Add(name, &field)) 372 Error("field already exists: " + name); 373 return field; 374} 375 376void Parser::ParseField(StructDef &struct_def) { 377 std::string name = attribute_; 378 std::vector<std::string> dc = doc_comment_; 379 Expect(kTokenIdentifier); 380 Expect(':'); 381 Type type; 382 ParseType(type); 383 384 if (struct_def.fixed && !IsScalar(type.base_type) && !IsStruct(type)) 385 Error("structs_ may contain only scalar or struct fields"); 386 387 FieldDef *typefield = nullptr; 388 if (type.base_type == BASE_TYPE_UNION) { 389 // For union fields, add a second auto-generated field to hold the type, 390 // with _type appended as the name. 391 typefield = &AddField(struct_def, name + "_type", 392 type.enum_def->underlying_type); 393 } 394 395 auto &field = AddField(struct_def, name, type); 396 397 if (token_ == '=') { 398 Next(); 399 if (!IsScalar(type.base_type)) 400 Error("default values currently only supported for scalars"); 401 ParseSingleValue(field.value); 402 } 403 404 if (type.enum_def && 405 IsScalar(type.base_type) && 406 !struct_def.fixed && 407 !type.enum_def->attributes.Lookup("bit_flags") && 408 !type.enum_def->ReverseLookup(static_cast<int>( 409 StringToInt(field.value.constant.c_str())))) 410 Error("enum " + type.enum_def->name + 411 " does not have a declaration for this field\'s default of " + 412 field.value.constant); 413 414 field.doc_comment = dc; 415 ParseMetaData(field); 416 field.deprecated = field.attributes.Lookup("deprecated") != nullptr; 417 auto hash_name = field.attributes.Lookup("hash"); 418 if (hash_name) { 419 switch (type.base_type) { 420 case BASE_TYPE_INT: 421 case BASE_TYPE_UINT: { 422 if (FindHashFunction32(hash_name->constant.c_str()) == nullptr) 423 Error("Unknown hashing algorithm for 32 bit types: " + 424 hash_name->constant); 425 break; 426 } 427 case BASE_TYPE_LONG: 428 case BASE_TYPE_ULONG: { 429 if (FindHashFunction64(hash_name->constant.c_str()) == nullptr) 430 Error("Unknown hashing algorithm for 64 bit types: " + 431 hash_name->constant); 432 break; 433 } 434 default: 435 Error("only int, uint, long and ulong data types support hashing."); 436 } 437 } 438 if (field.deprecated && struct_def.fixed) 439 Error("can't deprecate fields in a struct"); 440 field.required = field.attributes.Lookup("required") != nullptr; 441 if (field.required && (struct_def.fixed || 442 IsScalar(field.value.type.base_type))) 443 Error("only non-scalar fields in tables may be 'required'"); 444 field.key = field.attributes.Lookup("key") != nullptr; 445 if (field.key) { 446 if (struct_def.has_key) 447 Error("only one field may be set as 'key'"); 448 struct_def.has_key = true; 449 if (!IsScalar(field.value.type.base_type)) { 450 field.required = true; 451 if (field.value.type.base_type != BASE_TYPE_STRING) 452 Error("'key' field must be string or scalar type"); 453 } 454 } 455 auto nested = field.attributes.Lookup("nested_flatbuffer"); 456 if (nested) { 457 if (nested->type.base_type != BASE_TYPE_STRING) 458 Error("nested_flatbuffer attribute must be a string (the root type)"); 459 if (field.value.type.base_type != BASE_TYPE_VECTOR || 460 field.value.type.element != BASE_TYPE_UCHAR) 461 Error("nested_flatbuffer attribute may only apply to a vector of ubyte"); 462 // This will cause an error if the root type of the nested flatbuffer 463 // wasn't defined elsewhere. 464 LookupCreateStruct(nested->constant); 465 } 466 467 if (typefield) { 468 // If this field is a union, and it has a manually assigned id, 469 // the automatically added type field should have an id as well (of N - 1). 470 auto attr = field.attributes.Lookup("id"); 471 if (attr) { 472 auto id = atoi(attr->constant.c_str()); 473 auto val = new Value(); 474 val->type = attr->type; 475 val->constant = NumToString(id - 1); 476 typefield->attributes.Add("id", val); 477 } 478 } 479 480 Expect(';'); 481} 482 483void Parser::ParseAnyValue(Value &val, FieldDef *field) { 484 switch (val.type.base_type) { 485 case BASE_TYPE_UNION: { 486 assert(field); 487 if (!field_stack_.size() || 488 field_stack_.back().second->value.type.base_type != BASE_TYPE_UTYPE) 489 Error("missing type field before this union value: " + field->name); 490 auto enum_idx = atot<unsigned char>( 491 field_stack_.back().first.constant.c_str()); 492 auto enum_val = val.type.enum_def->ReverseLookup(enum_idx); 493 if (!enum_val) Error("illegal type id for: " + field->name); 494 val.constant = NumToString(ParseTable(*enum_val->struct_def)); 495 break; 496 } 497 case BASE_TYPE_STRUCT: 498 val.constant = NumToString(ParseTable(*val.type.struct_def)); 499 break; 500 case BASE_TYPE_STRING: { 501 auto s = attribute_; 502 Expect(kTokenStringConstant); 503 val.constant = NumToString(builder_.CreateString(s).o); 504 break; 505 } 506 case BASE_TYPE_VECTOR: { 507 Expect('['); 508 val.constant = NumToString(ParseVector(val.type.VectorType())); 509 break; 510 } 511 case BASE_TYPE_INT: 512 case BASE_TYPE_UINT: 513 case BASE_TYPE_LONG: 514 case BASE_TYPE_ULONG: { 515 if (field && field->attributes.Lookup("hash") && 516 (token_ == kTokenIdentifier || token_ == kTokenStringConstant)) { 517 ParseHash(val, field); 518 } else { 519 ParseSingleValue(val); 520 } 521 break; 522 } 523 default: 524 ParseSingleValue(val); 525 break; 526 } 527} 528 529void Parser::SerializeStruct(const StructDef &struct_def, const Value &val) { 530 auto off = atot<uoffset_t>(val.constant.c_str()); 531 assert(struct_stack_.size() - off == struct_def.bytesize); 532 builder_.Align(struct_def.minalign); 533 builder_.PushBytes(&struct_stack_[off], struct_def.bytesize); 534 struct_stack_.resize(struct_stack_.size() - struct_def.bytesize); 535 builder_.AddStructOffset(val.offset, builder_.GetSize()); 536} 537 538uoffset_t Parser::ParseTable(const StructDef &struct_def) { 539 Expect('{'); 540 size_t fieldn = 0; 541 for (;;) { 542 if ((!strict_json_ || !fieldn) && IsNext('}')) break; 543 std::string name = attribute_; 544 if (!IsNext(kTokenStringConstant)) 545 Expect(strict_json_ ? kTokenStringConstant : kTokenIdentifier); 546 auto field = struct_def.fields.Lookup(name); 547 if (!field) Error("unknown field: " + name); 548 if (struct_def.fixed && (fieldn >= struct_def.fields.vec.size() 549 || struct_def.fields.vec[fieldn] != field)) { 550 Error("struct field appearing out of order: " + name); 551 } 552 Expect(':'); 553 Value val = field->value; 554 ParseAnyValue(val, field); 555 field_stack_.push_back(std::make_pair(val, field)); 556 fieldn++; 557 if (IsNext('}')) break; 558 Expect(','); 559 } 560 for (auto it = field_stack_.rbegin(); 561 it != field_stack_.rbegin() + fieldn; ++it) { 562 if (it->second->used) 563 Error("field set more than once: " + it->second->name); 564 it->second->used = true; 565 } 566 for (auto it = field_stack_.rbegin(); 567 it != field_stack_.rbegin() + fieldn; ++it) { 568 it->second->used = false; 569 } 570 if (struct_def.fixed && fieldn != struct_def.fields.vec.size()) 571 Error("incomplete struct initialization: " + struct_def.name); 572 auto start = struct_def.fixed 573 ? builder_.StartStruct(struct_def.minalign) 574 : builder_.StartTable(); 575 576 for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1; 577 size; 578 size /= 2) { 579 // Go through elements in reverse, since we're building the data backwards. 580 for (auto it = field_stack_.rbegin(); 581 it != field_stack_.rbegin() + fieldn; ++it) { 582 auto &value = it->first; 583 auto field = it->second; 584 if (!struct_def.sortbysize || size == SizeOf(value.type.base_type)) { 585 switch (value.type.base_type) { 586 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \ 587 PTYPE) \ 588 case BASE_TYPE_ ## ENUM: \ 589 builder_.Pad(field->padding); \ 590 if (struct_def.fixed) { \ 591 builder_.PushElement(atot<CTYPE>(value.constant.c_str())); \ 592 } else { \ 593 builder_.AddElement(value.offset, \ 594 atot<CTYPE>( value.constant.c_str()), \ 595 atot<CTYPE>(field->value.constant.c_str())); \ 596 } \ 597 break; 598 FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD); 599 #undef FLATBUFFERS_TD 600 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \ 601 PTYPE) \ 602 case BASE_TYPE_ ## ENUM: \ 603 builder_.Pad(field->padding); \ 604 if (IsStruct(field->value.type)) { \ 605 SerializeStruct(*field->value.type.struct_def, value); \ 606 } else { \ 607 builder_.AddOffset(value.offset, \ 608 atot<CTYPE>(value.constant.c_str())); \ 609 } \ 610 break; 611 FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD); 612 #undef FLATBUFFERS_TD 613 } 614 } 615 } 616 } 617 for (size_t i = 0; i < fieldn; i++) field_stack_.pop_back(); 618 619 if (struct_def.fixed) { 620 builder_.ClearOffsets(); 621 builder_.EndStruct(); 622 // Temporarily store this struct in a side buffer, since this data has to 623 // be stored in-line later in the parent object. 624 auto off = struct_stack_.size(); 625 struct_stack_.insert(struct_stack_.end(), 626 builder_.GetBufferPointer(), 627 builder_.GetBufferPointer() + struct_def.bytesize); 628 builder_.PopBytes(struct_def.bytesize); 629 return static_cast<uoffset_t>(off); 630 } else { 631 return builder_.EndTable( 632 start, 633 static_cast<voffset_t>(struct_def.fields.vec.size())); 634 } 635} 636 637uoffset_t Parser::ParseVector(const Type &type) { 638 int count = 0; 639 for (;;) { 640 if ((!strict_json_ || !count) && IsNext(']')) break; 641 Value val; 642 val.type = type; 643 ParseAnyValue(val, nullptr); 644 field_stack_.push_back(std::make_pair(val, nullptr)); 645 count++; 646 if (IsNext(']')) break; 647 Expect(','); 648 } 649 650 builder_.StartVector(count * InlineSize(type) / InlineAlignment(type), 651 InlineAlignment(type)); 652 for (int i = 0; i < count; i++) { 653 // start at the back, since we're building the data backwards. 654 auto &val = field_stack_.back().first; 655 switch (val.type.base_type) { 656 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ 657 case BASE_TYPE_ ## ENUM: \ 658 if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \ 659 else builder_.PushElement(atot<CTYPE>(val.constant.c_str())); \ 660 break; 661 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) 662 #undef FLATBUFFERS_TD 663 } 664 field_stack_.pop_back(); 665 } 666 667 builder_.ClearOffsets(); 668 return builder_.EndVector(count); 669} 670 671void Parser::ParseMetaData(Definition &def) { 672 if (IsNext('(')) { 673 for (;;) { 674 auto name = attribute_; 675 Expect(kTokenIdentifier); 676 if (known_attributes_.find(name) == known_attributes_.end()) 677 Error("user define attributes must be declared before use: " + name); 678 auto e = new Value(); 679 def.attributes.Add(name, e); 680 if (IsNext(':')) { 681 ParseSingleValue(*e); 682 } 683 if (IsNext(')')) break; 684 Expect(','); 685 } 686 } 687} 688 689bool Parser::TryTypedValue(int dtoken, 690 bool check, 691 Value &e, 692 BaseType req) { 693 bool match = dtoken == token_; 694 if (match) { 695 e.constant = attribute_; 696 if (!check) { 697 if (e.type.base_type == BASE_TYPE_NONE) { 698 e.type.base_type = req; 699 } else { 700 Error(std::string("type mismatch: expecting: ") + 701 kTypeNames[e.type.base_type] + 702 ", found: " + 703 kTypeNames[req]); 704 } 705 } 706 Next(); 707 } 708 return match; 709} 710 711int64_t Parser::ParseIntegerFromString(Type &type) { 712 int64_t result = 0; 713 // Parse one or more enum identifiers, separated by spaces. 714 const char *next = attribute_.c_str(); 715 do { 716 const char *divider = strchr(next, ' '); 717 std::string word; 718 if (divider) { 719 word = std::string(next, divider); 720 next = divider + strspn(divider, " "); 721 } else { 722 word = next; 723 next += word.length(); 724 } 725 if (type.enum_def) { // The field has an enum type 726 auto enum_val = type.enum_def->vals.Lookup(word); 727 if (!enum_val) 728 Error("unknown enum value: " + word + 729 ", for enum: " + type.enum_def->name); 730 result |= enum_val->value; 731 } else { // No enum type, probably integral field. 732 if (!IsInteger(type.base_type)) 733 Error("not a valid value for this field: " + word); 734 // TODO: could check if its a valid number constant here. 735 const char *dot = strrchr(word.c_str(), '.'); 736 if (!dot) Error("enum values need to be qualified by an enum type"); 737 std::string enum_def_str(word.c_str(), dot); 738 std::string enum_val_str(dot + 1, word.c_str() + word.length()); 739 auto enum_def = LookupEnum(enum_def_str); 740 if (!enum_def) Error("unknown enum: " + enum_def_str); 741 auto enum_val = enum_def->vals.Lookup(enum_val_str); 742 if (!enum_val) Error("unknown enum value: " + enum_val_str); 743 result |= enum_val->value; 744 } 745 } while(*next); 746 return result; 747} 748 749 750void Parser::ParseHash(Value &e, FieldDef* field) { 751 assert(field); 752 Value *hash_name = field->attributes.Lookup("hash"); 753 switch (e.type.base_type) { 754 case BASE_TYPE_INT: 755 case BASE_TYPE_UINT: { 756 auto hash = FindHashFunction32(hash_name->constant.c_str()); 757 uint32_t hashed_value = hash(attribute_.c_str()); 758 e.constant = NumToString(hashed_value); 759 break; 760 } 761 case BASE_TYPE_LONG: 762 case BASE_TYPE_ULONG: { 763 auto hash = FindHashFunction64(hash_name->constant.c_str()); 764 uint64_t hashed_value = hash(attribute_.c_str()); 765 e.constant = NumToString(hashed_value); 766 break; 767 } 768 default: 769 assert(0); 770 } 771 Next(); 772} 773 774void Parser::ParseSingleValue(Value &e) { 775 // First check if this could be a string/identifier enum value: 776 if (e.type.base_type != BASE_TYPE_STRING && 777 e.type.base_type != BASE_TYPE_NONE && 778 (token_ == kTokenIdentifier || token_ == kTokenStringConstant)) { 779 e.constant = NumToString(ParseIntegerFromString(e.type)); 780 Next(); 781 } else if (TryTypedValue(kTokenIntegerConstant, 782 IsScalar(e.type.base_type), 783 e, 784 BASE_TYPE_INT) || 785 TryTypedValue(kTokenFloatConstant, 786 IsFloat(e.type.base_type), 787 e, 788 BASE_TYPE_FLOAT) || 789 TryTypedValue(kTokenStringConstant, 790 e.type.base_type == BASE_TYPE_STRING, 791 e, 792 BASE_TYPE_STRING)) { 793 } else { 794 Error("cannot parse value starting with: " + TokenToString(token_)); 795 } 796} 797 798StructDef *Parser::LookupCreateStruct(const std::string &name) { 799 std::string qualified_name = GetFullyQualifiedName(name); 800 auto struct_def = structs_.Lookup(qualified_name); 801 // Unqualified names may simply have no namespace at all, so try that too. 802 if (!struct_def) struct_def = structs_.Lookup(name); 803 if (!struct_def) { 804 // Rather than failing, we create a "pre declared" StructDef, due to 805 // circular references, and check for errors at the end of parsing. 806 struct_def = new StructDef(); 807 structs_.Add(qualified_name, struct_def); 808 struct_def->name = name; 809 struct_def->predecl = true; 810 struct_def->defined_namespace = namespaces_.back(); 811 } 812 return struct_def; 813} 814 815void Parser::ParseEnum(bool is_union) { 816 std::vector<std::string> enum_comment = doc_comment_; 817 Next(); 818 std::string enum_name = attribute_; 819 Expect(kTokenIdentifier); 820 auto &enum_def = *new EnumDef(); 821 enum_def.name = enum_name; 822 if (!files_being_parsed_.empty()) enum_def.file = files_being_parsed_.top(); 823 enum_def.doc_comment = enum_comment; 824 enum_def.is_union = is_union; 825 enum_def.defined_namespace = namespaces_.back(); 826 if (enums_.Add(GetFullyQualifiedName(enum_name), &enum_def)) 827 Error("enum already exists: " + enum_name); 828 if (is_union) { 829 enum_def.underlying_type.base_type = BASE_TYPE_UTYPE; 830 enum_def.underlying_type.enum_def = &enum_def; 831 } else { 832 if (proto_mode_) { 833 enum_def.underlying_type.base_type = BASE_TYPE_SHORT; 834 } else { 835 // Give specialized error message, since this type spec used to 836 // be optional in the first FlatBuffers release. 837 if (!IsNext(':')) Error("must specify the underlying integer type for this" 838 " enum (e.g. \': short\', which was the default)."); 839 // Specify the integer type underlying this enum. 840 ParseType(enum_def.underlying_type); 841 if (!IsInteger(enum_def.underlying_type.base_type)) 842 Error("underlying enum type must be integral"); 843 } 844 // Make this type refer back to the enum it was derived from. 845 enum_def.underlying_type.enum_def = &enum_def; 846 } 847 ParseMetaData(enum_def); 848 Expect('{'); 849 if (is_union) enum_def.vals.Add("NONE", new EnumVal("NONE", 0)); 850 do { 851 auto value_name = attribute_; 852 auto full_name = value_name; 853 std::vector<std::string> value_comment = doc_comment_; 854 Expect(kTokenIdentifier); 855 if (is_union) ParseNamespacing(&full_name, &value_name); 856 auto prevsize = enum_def.vals.vec.size(); 857 auto value = enum_def.vals.vec.size() 858 ? enum_def.vals.vec.back()->value + 1 859 : 0; 860 auto &ev = *new EnumVal(value_name, value); 861 if (enum_def.vals.Add(value_name, &ev)) 862 Error("enum value already exists: " + value_name); 863 ev.doc_comment = value_comment; 864 if (is_union) { 865 ev.struct_def = LookupCreateStruct(full_name); 866 } 867 if (IsNext('=')) { 868 ev.value = atoi(attribute_.c_str()); 869 Expect(kTokenIntegerConstant); 870 if (prevsize && enum_def.vals.vec[prevsize - 1]->value >= ev.value) 871 Error("enum values must be specified in ascending order"); 872 } 873 } while (IsNext(proto_mode_ ? ';' : ',') && token_ != '}'); 874 Expect('}'); 875 if (enum_def.attributes.Lookup("bit_flags")) { 876 for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); 877 ++it) { 878 if (static_cast<size_t>((*it)->value) >= 879 SizeOf(enum_def.underlying_type.base_type) * 8) 880 Error("bit flag out of range of underlying integral type"); 881 (*it)->value = 1LL << (*it)->value; 882 } 883 } 884} 885 886StructDef &Parser::StartStruct() { 887 std::string name = attribute_; 888 Expect(kTokenIdentifier); 889 auto &struct_def = *LookupCreateStruct(name); 890 if (!struct_def.predecl) Error("datatype already exists: " + name); 891 struct_def.predecl = false; 892 struct_def.name = name; 893 if (!files_being_parsed_.empty()) struct_def.file = files_being_parsed_.top(); 894 // Move this struct to the back of the vector just in case it was predeclared, 895 // to preserve declaration order. 896 *remove(structs_.vec.begin(), structs_.vec.end(), &struct_def) = &struct_def; 897 return struct_def; 898} 899 900void Parser::ParseDecl() { 901 std::vector<std::string> dc = doc_comment_; 902 bool fixed = IsNext(kTokenStruct); 903 if (!fixed) Expect(kTokenTable); 904 auto &struct_def = StartStruct(); 905 struct_def.doc_comment = dc; 906 struct_def.fixed = fixed; 907 ParseMetaData(struct_def); 908 struct_def.sortbysize = 909 struct_def.attributes.Lookup("original_order") == nullptr && !fixed; 910 Expect('{'); 911 while (token_ != '}') ParseField(struct_def); 912 auto force_align = struct_def.attributes.Lookup("force_align"); 913 if (fixed && force_align) { 914 auto align = static_cast<size_t>(atoi(force_align->constant.c_str())); 915 if (force_align->type.base_type != BASE_TYPE_INT || 916 align < struct_def.minalign || 917 align > 256 || 918 align & (align - 1)) 919 Error("force_align must be a power of two integer ranging from the" 920 "struct\'s natural alignment to 256"); 921 struct_def.minalign = align; 922 } 923 struct_def.PadLastField(struct_def.minalign); 924 // Check if this is a table that has manual id assignments 925 auto &fields = struct_def.fields.vec; 926 if (!struct_def.fixed && fields.size()) { 927 size_t num_id_fields = 0; 928 for (auto it = fields.begin(); it != fields.end(); ++it) { 929 if ((*it)->attributes.Lookup("id")) num_id_fields++; 930 } 931 // If any fields have ids.. 932 if (num_id_fields) { 933 // Then all fields must have them. 934 if (num_id_fields != fields.size()) 935 Error("either all fields or no fields must have an 'id' attribute"); 936 // Simply sort by id, then the fields are the same as if no ids had 937 // been specified. 938 std::sort(fields.begin(), fields.end(), 939 [](const FieldDef *a, const FieldDef *b) -> bool { 940 auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str()); 941 auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str()); 942 return a_id < b_id; 943 }); 944 // Verify we have a contiguous set, and reassign vtable offsets. 945 for (int i = 0; i < static_cast<int>(fields.size()); i++) { 946 if (i != atoi(fields[i]->attributes.Lookup("id")->constant.c_str())) 947 Error("field id\'s must be consecutive from 0, id " + 948 NumToString(i) + " missing or set twice"); 949 fields[i]->value.offset = FieldIndexToOffset(static_cast<voffset_t>(i)); 950 } 951 } 952 } 953 // Check that no identifiers clash with auto generated fields. 954 // This is not an ideal situation, but should occur very infrequently, 955 // and allows us to keep using very readable names for type & length fields 956 // without inducing compile errors. 957 auto CheckClash = [&fields, &struct_def](const char *suffix, 958 BaseType basetype) { 959 auto len = strlen(suffix); 960 for (auto it = fields.begin(); it != fields.end(); ++it) { 961 auto &name = (*it)->name; 962 if (name.length() > len && 963 name.compare(name.length() - len, len, suffix) == 0 && 964 (*it)->value.type.base_type != BASE_TYPE_UTYPE) { 965 auto field = struct_def.fields.Lookup( 966 name.substr(0, name.length() - len)); 967 if (field && field->value.type.base_type == basetype) 968 Error("Field " + name + 969 " would clash with generated functions for field " + 970 field->name); 971 } 972 } 973 }; 974 CheckClash("_type", BASE_TYPE_UNION); 975 CheckClash("Type", BASE_TYPE_UNION); 976 CheckClash("_length", BASE_TYPE_VECTOR); 977 CheckClash("Length", BASE_TYPE_VECTOR); 978 CheckClash("_byte_vector", BASE_TYPE_STRING); 979 CheckClash("ByteVector", BASE_TYPE_STRING); 980 Expect('}'); 981} 982 983bool Parser::SetRootType(const char *name) { 984 root_struct_def = structs_.Lookup(GetFullyQualifiedName(name)); 985 return root_struct_def != nullptr; 986} 987 988std::string Parser::GetFullyQualifiedName(const std::string &name) const { 989 Namespace *ns = namespaces_.back(); 990 991 // Early exit if we don't have a defined namespace, or if the name is already 992 // partially qualified 993 if (ns->components.size() == 0 || name.find(".") != std::string::npos) { 994 return name; 995 } 996 std::stringstream stream; 997 for (size_t i = 0; i != ns->components.size(); ++i) { 998 if (i != 0) { 999 stream << "."; 1000 } 1001 stream << ns->components[i]; 1002 } 1003 1004 stream << "." << name; 1005 return stream.str(); 1006} 1007 1008void Parser::MarkGenerated() { 1009 // Since the Parser object retains definitions across files, we must 1010 // ensure we only output code for definitions once, in the file they are first 1011 // declared. This function marks all existing definitions as having already 1012 // been generated. 1013 for (auto it = enums_.vec.begin(); 1014 it != enums_.vec.end(); ++it) { 1015 (*it)->generated = true; 1016 } 1017 for (auto it = structs_.vec.begin(); 1018 it != structs_.vec.end(); ++it) { 1019 (*it)->generated = true; 1020 } 1021} 1022 1023void Parser::ParseNamespace() { 1024 Next(); 1025 auto ns = new Namespace(); 1026 namespaces_.push_back(ns); 1027 for (;;) { 1028 ns->components.push_back(attribute_); 1029 Expect(kTokenIdentifier); 1030 if (!IsNext('.')) break; 1031 } 1032 Expect(';'); 1033} 1034 1035// Best effort parsing of .proto declarations, with the aim to turn them 1036// in the closest corresponding FlatBuffer equivalent. 1037// We parse everything as identifiers instead of keywords, since we don't 1038// want protobuf keywords to become invalid identifiers in FlatBuffers. 1039void Parser::ParseProtoDecl() { 1040 if (attribute_ == "package") { 1041 // These are identical in syntax to FlatBuffer's namespace decl. 1042 ParseNamespace(); 1043 } else if (attribute_ == "message") { 1044 std::vector<std::string> struct_comment = doc_comment_; 1045 Next(); 1046 auto &struct_def = StartStruct(); 1047 struct_def.doc_comment = struct_comment; 1048 Expect('{'); 1049 while (token_ != '}') { 1050 std::vector<std::string> field_comment = doc_comment_; 1051 // Parse the qualifier. 1052 bool required = false; 1053 bool repeated = false; 1054 if (attribute_ == "optional") { 1055 // This is the default. 1056 } else if (attribute_ == "required") { 1057 required = true; 1058 } else if (attribute_ == "repeated") { 1059 repeated = true; 1060 } else { 1061 Error("expecting optional/required/repeated, got: " + attribute_); 1062 } 1063 Type type = ParseTypeFromProtoType(); 1064 // Repeated elements get mapped to a vector. 1065 if (repeated) { 1066 type.element = type.base_type; 1067 type.base_type = BASE_TYPE_VECTOR; 1068 } 1069 std::string name = attribute_; 1070 Expect(kTokenIdentifier); 1071 // Parse the field id. Since we're just translating schemas, not 1072 // any kind of binary compatibility, we can safely ignore these, and 1073 // assign our own. 1074 Expect('='); 1075 Expect(kTokenIntegerConstant); 1076 auto &field = AddField(struct_def, name, type); 1077 field.doc_comment = field_comment; 1078 field.required = required; 1079 // See if there's a default specified. 1080 if (IsNext('[')) { 1081 if (attribute_ != "default") Error("\'default\' expected"); 1082 Next(); 1083 Expect('='); 1084 field.value.constant = attribute_; 1085 Next(); 1086 Expect(']'); 1087 } 1088 Expect(';'); 1089 } 1090 Next(); 1091 } else if (attribute_ == "enum") { 1092 // These are almost the same, just with different terminator: 1093 ParseEnum(false); 1094 } else if (attribute_ == "import") { 1095 Next(); 1096 included_files_[attribute_] = true; 1097 Expect(kTokenStringConstant); 1098 Expect(';'); 1099 } else if (attribute_ == "option") { // Skip these. 1100 Next(); 1101 Expect(kTokenIdentifier); 1102 Expect('='); 1103 Next(); // Any single token. 1104 Expect(';'); 1105 } else { 1106 Error("don\'t know how to parse .proto declaration starting with " + 1107 attribute_); 1108 } 1109} 1110 1111// Parse a protobuf type, and map it to the corresponding FlatBuffer one. 1112Type Parser::ParseTypeFromProtoType() { 1113 Expect(kTokenIdentifier); 1114 struct type_lookup { const char *proto_type; BaseType fb_type; }; 1115 static type_lookup lookup[] = { 1116 { "float", BASE_TYPE_FLOAT }, { "double", BASE_TYPE_DOUBLE }, 1117 { "int32", BASE_TYPE_INT }, { "int64", BASE_TYPE_LONG }, 1118 { "uint32", BASE_TYPE_UINT }, { "uint64", BASE_TYPE_ULONG }, 1119 { "sint32", BASE_TYPE_INT }, { "sint64", BASE_TYPE_LONG }, 1120 { "fixed32", BASE_TYPE_UINT }, { "fixed64", BASE_TYPE_ULONG }, 1121 { "sfixed32", BASE_TYPE_INT }, { "sfixed64", BASE_TYPE_LONG }, 1122 { "bool", BASE_TYPE_BOOL }, 1123 { "string", BASE_TYPE_STRING }, 1124 { "bytes", BASE_TYPE_STRING }, 1125 { nullptr, BASE_TYPE_NONE } 1126 }; 1127 Type type; 1128 for (auto tl = lookup; tl->proto_type; tl++) { 1129 if (attribute_ == tl->proto_type) { 1130 type.base_type = tl->fb_type; 1131 Next(); 1132 return type; 1133 } 1134 } 1135 ParseTypeIdent(type); 1136 return type; 1137} 1138 1139bool Parser::Parse(const char *source, const char **include_paths, 1140 const char *source_filename) { 1141 if (source_filename && 1142 included_files_.find(source_filename) == included_files_.end()) { 1143 included_files_[source_filename] = true; 1144 files_included_per_file_[source_filename] = std::set<std::string>(); 1145 files_being_parsed_.push(source_filename); 1146 } 1147 if (!include_paths) { 1148 const char *current_directory[] = { "", nullptr }; 1149 include_paths = current_directory; 1150 } 1151 source_ = cursor_ = source; 1152 line_ = 1; 1153 error_.clear(); 1154 builder_.Clear(); 1155 try { 1156 Next(); 1157 // Includes must come first: 1158 while (IsNext(kTokenInclude)) { 1159 auto name = attribute_; 1160 Expect(kTokenStringConstant); 1161 // Look for the file in include_paths. 1162 std::string filepath; 1163 for (auto paths = include_paths; paths && *paths; paths++) { 1164 filepath = flatbuffers::ConCatPathFileName(*paths, name); 1165 if(FileExists(filepath.c_str())) break; 1166 } 1167 if (filepath.empty()) 1168 Error("unable to locate include file: " + name); 1169 if (source_filename) 1170 files_included_per_file_[source_filename].insert(filepath); 1171 if (included_files_.find(filepath) == included_files_.end()) { 1172 // We found an include file that we have not parsed yet. 1173 // Load it and parse it. 1174 std::string contents; 1175 if (!LoadFile(filepath.c_str(), true, &contents)) 1176 Error("unable to load include file: " + name); 1177 if (!Parse(contents.c_str(), include_paths, filepath.c_str())) { 1178 // Any errors, we're done. 1179 return false; 1180 } 1181 // We do not want to output code for any included files: 1182 MarkGenerated(); 1183 // This is the easiest way to continue this file after an include: 1184 // instead of saving and restoring all the state, we simply start the 1185 // file anew. This will cause it to encounter the same include statement 1186 // again, but this time it will skip it, because it was entered into 1187 // included_files_. 1188 // This is recursive, but only go as deep as the number of include 1189 // statements. 1190 return Parse(source, include_paths, source_filename); 1191 } 1192 Expect(';'); 1193 } 1194 // Start with a blank namespace just in case this file doesn't have one. 1195 namespaces_.push_back(new Namespace()); 1196 // Now parse all other kinds of declarations: 1197 while (token_ != kTokenEof) { 1198 if (proto_mode_) { 1199 ParseProtoDecl(); 1200 } else if (token_ == kTokenNameSpace) { 1201 ParseNamespace(); 1202 } else if (token_ == '{') { 1203 if (!root_struct_def) Error("no root type set to parse json with"); 1204 if (builder_.GetSize()) { 1205 Error("cannot have more than one json object in a file"); 1206 } 1207 builder_.Finish(Offset<Table>(ParseTable(*root_struct_def)), 1208 file_identifier_.length() ? file_identifier_.c_str() : nullptr); 1209 } else if (token_ == kTokenEnum) { 1210 ParseEnum(false); 1211 } else if (token_ == kTokenUnion) { 1212 ParseEnum(true); 1213 } else if (token_ == kTokenRootType) { 1214 Next(); 1215 auto root_type = attribute_; 1216 Expect(kTokenIdentifier); 1217 if (!SetRootType(root_type.c_str())) 1218 Error("unknown root type: " + root_type); 1219 if (root_struct_def->fixed) 1220 Error("root type must be a table"); 1221 Expect(';'); 1222 } else if (token_ == kTokenFileIdentifier) { 1223 Next(); 1224 file_identifier_ = attribute_; 1225 Expect(kTokenStringConstant); 1226 if (file_identifier_.length() != 1227 FlatBufferBuilder::kFileIdentifierLength) 1228 Error("file_identifier must be exactly " + 1229 NumToString(FlatBufferBuilder::kFileIdentifierLength) + 1230 " characters"); 1231 Expect(';'); 1232 } else if (token_ == kTokenFileExtension) { 1233 Next(); 1234 file_extension_ = attribute_; 1235 Expect(kTokenStringConstant); 1236 Expect(';'); 1237 } else if(token_ == kTokenInclude) { 1238 Error("includes must come before declarations"); 1239 } else if(token_ == kTokenAttribute) { 1240 Next(); 1241 auto name = attribute_; 1242 Expect(kTokenStringConstant); 1243 Expect(';'); 1244 known_attributes_.insert(name); 1245 } else { 1246 ParseDecl(); 1247 } 1248 } 1249 for (auto it = structs_.vec.begin(); it != structs_.vec.end(); ++it) { 1250 if ((*it)->predecl) 1251 Error("type referenced but not defined: " + (*it)->name); 1252 } 1253 for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) { 1254 auto &enum_def = **it; 1255 if (enum_def.is_union) { 1256 for (auto val_it = enum_def.vals.vec.begin(); 1257 val_it != enum_def.vals.vec.end(); 1258 ++val_it) { 1259 auto &val = **val_it; 1260 if (val.struct_def && val.struct_def->fixed) 1261 Error("only tables can be union elements: " + val.name); 1262 } 1263 } 1264 } 1265 } catch (const std::string &msg) { 1266 error_ = source_filename ? AbsolutePath(source_filename) : ""; 1267 #ifdef _WIN32 1268 error_ += "(" + NumToString(line_) + ")"; // MSVC alike 1269 #else 1270 if (source_filename) error_ += ":"; 1271 error_ += NumToString(line_) + ":0"; // gcc alike 1272 #endif 1273 error_ += ": error: " + msg; 1274 if (source_filename) files_being_parsed_.pop(); 1275 return false; 1276 } 1277 if (source_filename) files_being_parsed_.pop(); 1278 assert(!struct_stack_.size()); 1279 return true; 1280} 1281 1282std::set<std::string> Parser::GetIncludedFilesRecursive( 1283 const std::string &file_name) const { 1284 std::set<std::string> included_files; 1285 std::list<std::string> to_process; 1286 1287 if (file_name.empty()) return included_files; 1288 to_process.push_back(file_name); 1289 1290 while (!to_process.empty()) { 1291 std::string current = to_process.front(); 1292 to_process.pop_front(); 1293 included_files.insert(current); 1294 1295 auto new_files = files_included_per_file_.at(current); 1296 for (auto it = new_files.begin(); it != new_files.end(); ++it) { 1297 if (included_files.find(*it) == included_files.end()) 1298 to_process.push_back(*it); 1299 } 1300 } 1301 1302 return included_files; 1303} 1304 1305} // namespace flatbuffers 1306