idl_parser.cpp 40 KB

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