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