blob: 3d57b6bab1adde855fbd996bbfafd25df5bc3b35 [file] [log] [blame]
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001/*
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>
Gabriel Martinezdf4909e2014-11-04 10:00:56 -080018#include <list>
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080019
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080020#include "flatbuffers/idl.h"
21#include "flatbuffers/util.h"
22
23namespace flatbuffers {
24
25const char *const kTypeNames[] = {
rw48dfc692014-12-16 00:32:11 -080026 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
27 IDLTYPE,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080028 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
29 #undef FLATBUFFERS_TD
30 nullptr
31};
32
33const char kTypeSizes[] = {
rw48dfc692014-12-16 00:32:11 -080034 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
Wouter van Oortmerssen557c88c2014-09-16 17:37:17 -070035 sizeof(CTYPE),
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080036 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
37 #undef FLATBUFFERS_TD
38};
39
Wouter van Oortmerssen81312c22015-05-21 16:33:29 -070040// The enums in the reflection schema should match the ones we use internally.
41// Compare the last element to check if these go out of sync.
42static_assert(BASE_TYPE_UNION ==
Wouter van Oortmerssen622b8d02015-06-15 15:57:48 -070043 static_cast<BaseType>(reflection::Union),
Wouter van Oortmerssen81312c22015-05-21 16:33:29 -070044 "enums don't match");
45
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080046static void Error(const std::string &msg) {
47 throw msg;
48}
49
50// Ensure that integer values we parse fit inside the declared integer type.
51static void CheckBitsFit(int64_t val, size_t bits) {
Wouter van Oortmerssenaeff09d2015-08-26 16:47:59 -070052 // Bits we allow to be used.
53 auto mask = static_cast<int64_t>((1ull << bits) - 1);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080054 if (bits < 64 &&
55 (val & ~mask) != 0 && // Positive or unsigned.
56 (val | mask) != -1) // Negative.
57 Error("constant does not fit in a " + NumToString(bits) + "-bit field");
58}
59
60// atot: templated version of atoi/atof: convert a string to an instance of T.
61template<typename T> inline T atot(const char *s) {
62 auto val = StringToInt(s);
63 CheckBitsFit(val, sizeof(T) * 8);
64 return (T)val;
65}
66template<> inline bool atot<bool>(const char *s) {
67 return 0 != atoi(s);
68}
69template<> inline float atot<float>(const char *s) {
70 return static_cast<float>(strtod(s, nullptr));
71}
72template<> inline double atot<double>(const char *s) {
73 return strtod(s, nullptr);
74}
75
76template<> inline Offset<void> atot<Offset<void>>(const char *s) {
77 return Offset<void>(atoi(s));
78}
79
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -070080std::string Namespace::GetFullyQualifiedName(const std::string &name,
81 size_t max_components) const {
82 // Early exit if we don't have a defined namespace.
83 if (components.size() == 0 || !max_components) {
84 return name;
85 }
86 std::stringstream stream;
87 for (size_t i = 0; i < std::min(components.size(), max_components);
88 i++) {
89 if (i) {
90 stream << ".";
91 }
92 stream << components[i];
93 }
94
95 stream << "." << name;
96 return stream.str();
97}
98
99
100
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800101// Declare tokens we'll use. Single character tokens are represented by their
102// ascii character code (e.g. '{'), others above 256.
103#define FLATBUFFERS_GEN_TOKENS(TD) \
104 TD(Eof, 256, "end of file") \
105 TD(StringConstant, 257, "string constant") \
106 TD(IntegerConstant, 258, "integer constant") \
107 TD(FloatConstant, 259, "float constant") \
108 TD(Identifier, 260, "identifier") \
109 TD(Table, 261, "table") \
110 TD(Struct, 262, "struct") \
111 TD(Enum, 263, "enum") \
112 TD(Union, 264, "union") \
113 TD(NameSpace, 265, "namespace") \
Wouter van Oortmerssen5da7bda2014-07-31 15:11:03 -0700114 TD(RootType, 266, "root_type") \
115 TD(FileIdentifier, 267, "file_identifier") \
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700116 TD(FileExtension, 268, "file_extension") \
Wouter van Oortmerssen09521432014-11-17 17:27:26 -0800117 TD(Include, 269, "include") \
118 TD(Attribute, 270, "attribute")
Wouter van Oortmerssen8f80fec2014-07-29 10:29:38 -0700119#ifdef __GNUC__
120__extension__ // Stop GCC complaining about trailing comma with -Wpendantic.
121#endif
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800122enum {
Wouter van Oortmerssen75349ae2014-07-09 11:44:26 -0700123 #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) kToken ## NAME = VALUE,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800124 FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
125 #undef FLATBUFFERS_TOKEN
rw48dfc692014-12-16 00:32:11 -0800126 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
Wouter van Oortmerssen557c88c2014-09-16 17:37:17 -0700127 kToken ## ENUM,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800128 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
129 #undef FLATBUFFERS_TD
130};
131
132static std::string TokenToString(int t) {
133 static const char *tokens[] = {
134 #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) STRING,
135 FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
136 #undef FLATBUFFERS_TOKEN
rw48dfc692014-12-16 00:32:11 -0800137 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
138 IDLTYPE,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800139 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
140 #undef FLATBUFFERS_TD
141 };
142 if (t < 256) { // A single ascii char token.
143 std::string s;
Wouter van Oortmerssen8e409022014-09-02 17:32:12 -0700144 s.append(1, static_cast<char>(t));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800145 return s;
146 } else { // Other tokens.
147 return tokens[t - 256];
148 }
149}
150
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700151std::string Parser::TokenToStringId(int t) {
152 return TokenToString(t) + (t == kTokenIdentifier ? ": " + attribute_ : "");
153}
154
Wouter van Oortmerssenebac1e12014-08-20 13:22:16 -0700155// Parses exactly nibbles worth of hex digits into a number, or error.
156int64_t Parser::ParseHexNum(int nibbles) {
157 for (int i = 0; i < nibbles; i++)
158 if (!isxdigit(cursor_[i]))
159 Error("escape code must be followed by " + NumToString(nibbles) +
160 " hex digits");
Hiroshi Matsunaga7cf74cb2015-01-09 03:38:14 +0900161 std::string target(cursor_, cursor_ + nibbles);
Wouter van Oortmerssen1917e572015-10-19 15:55:28 -0700162 auto val = StringToUInt(target.c_str(), 16);
Wouter van Oortmerssenebac1e12014-08-20 13:22:16 -0700163 cursor_ += nibbles;
164 return val;
165}
166
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800167void Parser::Next() {
168 doc_comment_.clear();
169 bool seen_newline = false;
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700170 attribute_.clear();
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800171 for (;;) {
172 char c = *cursor_++;
173 token_ = c;
174 switch (c) {
175 case '\0': cursor_--; token_ = kTokenEof; return;
176 case ' ': case '\r': case '\t': break;
177 case '\n': line_++; seen_newline = true; break;
178 case '{': case '}': case '(': case ')': case '[': case ']': return;
179 case ',': case ':': case ';': case '=': return;
180 case '.':
181 if(!isdigit(*cursor_)) return;
182 Error("floating point constant can\'t start with \".\"");
183 break;
184 case '\"':
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700185 case '\'':
186 while (*cursor_ != c) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800187 if (*cursor_ < ' ' && *cursor_ >= 0)
188 Error("illegal character in string constant");
189 if (*cursor_ == '\\') {
190 cursor_++;
191 switch (*cursor_) {
192 case 'n': attribute_ += '\n'; cursor_++; break;
193 case 't': attribute_ += '\t'; cursor_++; break;
194 case 'r': attribute_ += '\r'; cursor_++; break;
Wouter van Oortmerssenebac1e12014-08-20 13:22:16 -0700195 case 'b': attribute_ += '\b'; cursor_++; break;
196 case 'f': attribute_ += '\f'; cursor_++; break;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800197 case '\"': attribute_ += '\"'; cursor_++; break;
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700198 case '\'': attribute_ += '\''; cursor_++; break;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800199 case '\\': attribute_ += '\\'; cursor_++; break;
Wouter van Oortmerssenebac1e12014-08-20 13:22:16 -0700200 case '/': attribute_ += '/'; cursor_++; break;
201 case 'x': { // Not in the JSON standard
202 cursor_++;
203 attribute_ += static_cast<char>(ParseHexNum(2));
204 break;
205 }
206 case 'u': {
207 cursor_++;
208 ToUTF8(static_cast<int>(ParseHexNum(4)), &attribute_);
209 break;
210 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800211 default: Error("unknown escape code in string constant"); break;
212 }
213 } else { // printable chars + UTF-8 bytes
214 attribute_ += *cursor_++;
215 }
216 }
217 cursor_++;
218 token_ = kTokenStringConstant;
219 return;
220 case '/':
221 if (*cursor_ == '/') {
222 const char *start = ++cursor_;
Mormegila8d69622015-04-14 18:09:08 +0200223 while (*cursor_ && *cursor_ != '\n' && *cursor_ != '\r') cursor_++;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800224 if (*start == '/') { // documentation comment
Zbigniew Mandziejewicz07d59652014-10-22 22:40:03 +0800225 if (cursor_ != source_ && !seen_newline)
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800226 Error("a documentation comment should be on a line on its own");
Gabriel Martinez730c0ca2014-09-24 11:46:32 -0700227 doc_comment_.push_back(std::string(start + 1, cursor_));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800228 }
229 break;
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700230 } else if (*cursor_ == '*') {
231 cursor_++;
232 // TODO: make nested.
233 while (*cursor_ != '*' || cursor_[1] != '/') {
234 if (!*cursor_) Error("end of file in comment");
235 cursor_++;
236 }
237 cursor_ += 2;
238 break;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800239 }
240 // fall thru
241 default:
Wouter van Oortmerssen5f091c42015-06-15 12:17:19 -0700242 if (isalpha(static_cast<unsigned char>(c)) || c == '_') {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800243 // Collect all chars of an identifier:
244 const char *start = cursor_ - 1;
245 while (isalnum(static_cast<unsigned char>(*cursor_)) ||
246 *cursor_ == '_')
247 cursor_++;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800248 attribute_.append(start, cursor_);
249 // First, see if it is a type keyword from the table of types:
rw48dfc692014-12-16 00:32:11 -0800250 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \
251 PTYPE) \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800252 if (attribute_ == IDLTYPE) { \
253 token_ = kToken ## ENUM; \
254 return; \
255 }
256 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
257 #undef FLATBUFFERS_TD
258 // If it's a boolean constant keyword, turn those into integers,
259 // which simplifies our logic downstream.
260 if (attribute_ == "true" || attribute_ == "false") {
261 attribute_ = NumToString(attribute_ == "true");
262 token_ = kTokenIntegerConstant;
263 return;
264 }
265 // Check for declaration keywords:
266 if (attribute_ == "table") { token_ = kTokenTable; return; }
267 if (attribute_ == "struct") { token_ = kTokenStruct; return; }
268 if (attribute_ == "enum") { token_ = kTokenEnum; return; }
269 if (attribute_ == "union") { token_ = kTokenUnion; return; }
270 if (attribute_ == "namespace") { token_ = kTokenNameSpace; return; }
271 if (attribute_ == "root_type") { token_ = kTokenRootType; return; }
Wouter van Oortmerssen09521432014-11-17 17:27:26 -0800272 if (attribute_ == "include") { token_ = kTokenInclude; return; }
273 if (attribute_ == "attribute") { token_ = kTokenAttribute; return; }
Wouter van Oortmerssen5da7bda2014-07-31 15:11:03 -0700274 if (attribute_ == "file_identifier") {
275 token_ = kTokenFileIdentifier;
276 return;
277 }
278 if (attribute_ == "file_extension") {
279 token_ = kTokenFileExtension;
280 return;
281 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800282 // If not, it is a user-defined identifier:
283 token_ = kTokenIdentifier;
284 return;
285 } else if (isdigit(static_cast<unsigned char>(c)) || c == '-') {
286 const char *start = cursor_ - 1;
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700287 if (c == '0' && (*cursor_ == 'x' || *cursor_ == 'X')) {
288 cursor_++;
289 while (isxdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
290 attribute_.append(start + 2, cursor_);
Wouter van Oortmerssen1917e572015-10-19 15:55:28 -0700291 attribute_ = NumToString(StringToUInt(attribute_.c_str(), 16));
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700292 token_ = kTokenIntegerConstant;
293 return;
294 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800295 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700296 if (*cursor_ == '.' || *cursor_ == 'e' || *cursor_ == 'E') {
297 if (*cursor_ == '.') {
298 cursor_++;
299 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
300 }
Wouter van Oortmerssen93df5692014-07-10 13:40:55 -0700301 // See if this float has a scientific notation suffix. Both JSON
302 // and C++ (through strtod() we use) have the same format:
303 if (*cursor_ == 'e' || *cursor_ == 'E') {
304 cursor_++;
305 if (*cursor_ == '+' || *cursor_ == '-') cursor_++;
306 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
307 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800308 token_ = kTokenFloatConstant;
309 } else {
310 token_ = kTokenIntegerConstant;
311 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800312 attribute_.append(start, cursor_);
313 return;
314 }
315 std::string ch;
316 ch = c;
317 if (c < ' ' || c > '~') ch = "code: " + NumToString(c);
318 Error("illegal character: " + ch);
319 break;
320 }
321 }
322}
323
324// Check if a given token is next, if so, consume it as well.
325bool Parser::IsNext(int t) {
326 bool isnext = t == token_;
327 if (isnext) Next();
328 return isnext;
329}
330
331// Expect a given token to be next, consume it, or error if not present.
332void Parser::Expect(int t) {
333 if (t != token_) {
334 Error("expecting: " + TokenToString(t) + " instead got: " +
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700335 TokenToStringId(token_));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800336 }
337 Next();
338}
339
Wouter van Oortmerssen39833d72015-05-08 15:04:53 -0700340void Parser::ParseNamespacing(std::string *id, std::string *last) {
341 while (IsNext('.')) {
342 *id += ".";
343 *id += attribute_;
344 if (last) *last = attribute_;
345 Expect(kTokenIdentifier);
346 }
347}
348
349EnumDef *Parser::LookupEnum(const std::string &id) {
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700350 // Search thru parent namespaces.
351 for (int components = static_cast<int>(namespaces_.back()->components.size());
352 components >= 0; components--) {
353 auto ed = enums_.Lookup(namespaces_.back()->GetFullyQualifiedName(id, components));
354 if (ed) return ed;
355 }
356 return nullptr;
Wouter van Oortmerssen39833d72015-05-08 15:04:53 -0700357}
358
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -0700359void Parser::ParseTypeIdent(Type &type) {
Wouter van Oortmerssen39833d72015-05-08 15:04:53 -0700360 std::string id = attribute_;
361 Expect(kTokenIdentifier);
362 ParseNamespacing(&id, nullptr);
363 auto enum_def = LookupEnum(id);
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -0700364 if (enum_def) {
365 type = enum_def->underlying_type;
366 if (enum_def->is_union) type.base_type = BASE_TYPE_UNION;
367 } else {
368 type.base_type = BASE_TYPE_STRUCT;
Wouter van Oortmerssen39833d72015-05-08 15:04:53 -0700369 type.struct_def = LookupCreateStruct(id);
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -0700370 }
371}
372
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800373// Parse any IDL type.
374void Parser::ParseType(Type &type) {
375 if (token_ >= kTokenBOOL && token_ <= kTokenSTRING) {
376 type.base_type = static_cast<BaseType>(token_ - kTokenNONE);
Wouter van Oortmerssen39833d72015-05-08 15:04:53 -0700377 Next();
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800378 } else {
379 if (token_ == kTokenIdentifier) {
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -0700380 ParseTypeIdent(type);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800381 } else if (token_ == '[') {
382 Next();
383 Type subtype;
384 ParseType(subtype);
385 if (subtype.base_type == BASE_TYPE_VECTOR) {
386 // We could support this, but it will complicate things, and it's
387 // easier to work around with a struct around the inner vector.
388 Error("nested vector types not supported (wrap in table first).");
389 }
390 if (subtype.base_type == BASE_TYPE_UNION) {
391 // We could support this if we stored a struct of 2 elements per
392 // union element.
393 Error("vector of union types not supported (wrap in table first).");
394 }
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700395 type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800396 type.element = subtype.base_type;
397 Expect(']');
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800398 } else {
399 Error("illegal type syntax");
400 }
401 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800402}
403
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700404FieldDef &Parser::AddField(StructDef &struct_def, const std::string &name,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800405 const Type &type) {
406 auto &field = *new FieldDef();
407 field.value.offset =
408 FieldIndexToOffset(static_cast<voffset_t>(struct_def.fields.vec.size()));
409 field.name = name;
Gabriel Martinezdf4909e2014-11-04 10:00:56 -0800410 field.file = struct_def.file;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800411 field.value.type = type;
412 if (struct_def.fixed) { // statically compute the field offset
413 auto size = InlineSize(type);
414 auto alignment = InlineAlignment(type);
415 // structs_ need to have a predictable format, so we need to align to
416 // the largest scalar
417 struct_def.minalign = std::max(struct_def.minalign, alignment);
418 struct_def.PadLastField(alignment);
Wouter van Oortmerssen12563072014-06-30 15:56:31 -0700419 field.value.offset = static_cast<voffset_t>(struct_def.bytesize);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800420 struct_def.bytesize += size;
421 }
422 if (struct_def.fields.Add(name, &field))
423 Error("field already exists: " + name);
424 return field;
425}
426
427void Parser::ParseField(StructDef &struct_def) {
428 std::string name = attribute_;
Gabriel Martinez730c0ca2014-09-24 11:46:32 -0700429 std::vector<std::string> dc = doc_comment_;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800430 Expect(kTokenIdentifier);
431 Expect(':');
432 Type type;
433 ParseType(type);
434
435 if (struct_def.fixed && !IsScalar(type.base_type) && !IsStruct(type))
436 Error("structs_ may contain only scalar or struct fields");
437
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700438 FieldDef *typefield = nullptr;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800439 if (type.base_type == BASE_TYPE_UNION) {
440 // For union fields, add a second auto-generated field to hold the type,
441 // with _type appended as the name.
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700442 typefield = &AddField(struct_def, name + "_type",
443 type.enum_def->underlying_type);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800444 }
445
446 auto &field = AddField(struct_def, name, type);
447
448 if (token_ == '=') {
449 Next();
Wouter van Oortmerssen15dc1a82014-09-03 12:23:15 -0700450 if (!IsScalar(type.base_type))
451 Error("default values currently only supported for scalars");
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800452 ParseSingleValue(field.value);
453 }
454
Wouter van Oortmerssen7b805352014-09-23 11:55:42 -0700455 if (type.enum_def &&
456 IsScalar(type.base_type) &&
457 !struct_def.fixed &&
458 !type.enum_def->attributes.Lookup("bit_flags") &&
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -0700459 !type.enum_def->ReverseLookup(static_cast<int>(
460 StringToInt(field.value.constant.c_str()))))
Wouter van Oortmerssen7b805352014-09-23 11:55:42 -0700461 Error("enum " + type.enum_def->name +
462 " does not have a declaration for this field\'s default of " +
463 field.value.constant);
464
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800465 field.doc_comment = dc;
466 ParseMetaData(field);
467 field.deprecated = field.attributes.Lookup("deprecated") != nullptr;
Alex Amesd5753212015-02-13 15:58:29 -0800468 auto hash_name = field.attributes.Lookup("hash");
469 if (hash_name) {
470 switch (type.base_type) {
471 case BASE_TYPE_INT:
472 case BASE_TYPE_UINT: {
473 if (FindHashFunction32(hash_name->constant.c_str()) == nullptr)
474 Error("Unknown hashing algorithm for 32 bit types: " +
475 hash_name->constant);
476 break;
477 }
478 case BASE_TYPE_LONG:
479 case BASE_TYPE_ULONG: {
480 if (FindHashFunction64(hash_name->constant.c_str()) == nullptr)
481 Error("Unknown hashing algorithm for 64 bit types: " +
482 hash_name->constant);
483 break;
484 }
485 default:
486 Error("only int, uint, long and ulong data types support hashing.");
487 }
488 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800489 if (field.deprecated && struct_def.fixed)
490 Error("can't deprecate fields in a struct");
Wouter van Oortmerssen517c9642014-09-19 16:51:36 -0700491 field.required = field.attributes.Lookup("required") != nullptr;
492 if (field.required && (struct_def.fixed ||
493 IsScalar(field.value.type.base_type)))
494 Error("only non-scalar fields in tables may be 'required'");
Wouter van Oortmerssen35508992015-01-07 17:51:31 -0800495 field.key = field.attributes.Lookup("key") != nullptr;
496 if (field.key) {
497 if (struct_def.has_key)
498 Error("only one field may be set as 'key'");
499 struct_def.has_key = true;
500 if (!IsScalar(field.value.type.base_type)) {
501 field.required = true;
502 if (field.value.type.base_type != BASE_TYPE_STRING)
503 Error("'key' field must be string or scalar type");
504 }
505 }
Wouter van Oortmerssen3e201a92014-07-15 17:50:22 -0700506 auto nested = field.attributes.Lookup("nested_flatbuffer");
507 if (nested) {
508 if (nested->type.base_type != BASE_TYPE_STRING)
509 Error("nested_flatbuffer attribute must be a string (the root type)");
510 if (field.value.type.base_type != BASE_TYPE_VECTOR ||
511 field.value.type.element != BASE_TYPE_UCHAR)
512 Error("nested_flatbuffer attribute may only apply to a vector of ubyte");
513 // This will cause an error if the root type of the nested flatbuffer
514 // wasn't defined elsewhere.
515 LookupCreateStruct(nested->constant);
516 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800517
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700518 if (typefield) {
519 // If this field is a union, and it has a manually assigned id,
520 // the automatically added type field should have an id as well (of N - 1).
521 auto attr = field.attributes.Lookup("id");
522 if (attr) {
523 auto id = atoi(attr->constant.c_str());
524 auto val = new Value();
525 val->type = attr->type;
526 val->constant = NumToString(id - 1);
527 typefield->attributes.Add("id", val);
528 }
529 }
530
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800531 Expect(';');
532}
533
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700534void Parser::ParseAnyValue(Value &val, FieldDef *field, size_t parent_fieldn) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800535 switch (val.type.base_type) {
536 case BASE_TYPE_UNION: {
537 assert(field);
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700538 if (!parent_fieldn ||
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800539 field_stack_.back().second->value.type.base_type != BASE_TYPE_UTYPE)
540 Error("missing type field before this union value: " + field->name);
541 auto enum_idx = atot<unsigned char>(
542 field_stack_.back().first.constant.c_str());
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700543 auto enum_val = val.type.enum_def->ReverseLookup(enum_idx);
544 if (!enum_val) Error("illegal type id for: " + field->name);
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700545 ParseTable(*enum_val->struct_def, &val.constant);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800546 break;
547 }
548 case BASE_TYPE_STRUCT:
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700549 ParseTable(*val.type.struct_def, &val.constant);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800550 break;
551 case BASE_TYPE_STRING: {
552 auto s = attribute_;
553 Expect(kTokenStringConstant);
554 val.constant = NumToString(builder_.CreateString(s).o);
555 break;
556 }
557 case BASE_TYPE_VECTOR: {
558 Expect('[');
559 val.constant = NumToString(ParseVector(val.type.VectorType()));
560 break;
561 }
Alex Amesd5753212015-02-13 15:58:29 -0800562 case BASE_TYPE_INT:
563 case BASE_TYPE_UINT:
564 case BASE_TYPE_LONG:
565 case BASE_TYPE_ULONG: {
566 if (field && field->attributes.Lookup("hash") &&
567 (token_ == kTokenIdentifier || token_ == kTokenStringConstant)) {
568 ParseHash(val, field);
569 } else {
570 ParseSingleValue(val);
571 }
572 break;
573 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800574 default:
575 ParseSingleValue(val);
576 break;
577 }
578}
579
580void Parser::SerializeStruct(const StructDef &struct_def, const Value &val) {
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700581 assert(val.constant.length() == struct_def.bytesize);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800582 builder_.Align(struct_def.minalign);
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700583 builder_.PushBytes(reinterpret_cast<const uint8_t *>(val.constant.c_str()),
584 struct_def.bytesize);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800585 builder_.AddStructOffset(val.offset, builder_.GetSize());
586}
587
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700588uoffset_t Parser::ParseTable(const StructDef &struct_def, std::string *value) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800589 Expect('{');
590 size_t fieldn = 0;
Wouter van Oortmerssen6c2dc412015-01-16 16:57:04 -0800591 for (;;) {
592 if ((!strict_json_ || !fieldn) && IsNext('}')) break;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800593 std::string name = attribute_;
Wouter van Oortmerssen6c2dc412015-01-16 16:57:04 -0800594 if (!IsNext(kTokenStringConstant))
595 Expect(strict_json_ ? kTokenStringConstant : kTokenIdentifier);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800596 auto field = struct_def.fields.Lookup(name);
597 if (!field) Error("unknown field: " + name);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800598 Expect(':');
599 Value val = field->value;
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700600 ParseAnyValue(val, field, fieldn);
601 size_t i = field_stack_.size();
602 // Hardcoded insertion-sort with error-check.
603 // If fields are specified in order, then this loop exits immediately.
604 for (; i > field_stack_.size() - fieldn; i--) {
605 auto existing_field = field_stack_[i - 1].second;
606 if (existing_field == field)
607 Error("field set more than once: " + field->name);
608 if (existing_field->value.offset < field->value.offset) break;
609 }
610 field_stack_.insert(field_stack_.begin() + i, std::make_pair(val, field));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800611 fieldn++;
612 if (IsNext('}')) break;
613 Expect(',');
614 }
615 if (struct_def.fixed && fieldn != struct_def.fields.vec.size())
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700616 Error("struct: wrong number of initializers: " + struct_def.name);
617
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800618 auto start = struct_def.fixed
619 ? builder_.StartStruct(struct_def.minalign)
620 : builder_.StartTable();
621
622 for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
623 size;
624 size /= 2) {
625 // Go through elements in reverse, since we're building the data backwards.
626 for (auto it = field_stack_.rbegin();
627 it != field_stack_.rbegin() + fieldn; ++it) {
628 auto &value = it->first;
629 auto field = it->second;
630 if (!struct_def.sortbysize || size == SizeOf(value.type.base_type)) {
631 switch (value.type.base_type) {
rw48dfc692014-12-16 00:32:11 -0800632 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \
633 PTYPE) \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800634 case BASE_TYPE_ ## ENUM: \
635 builder_.Pad(field->padding); \
Wouter van Oortmerssenbe3c8742014-08-11 17:38:54 -0700636 if (struct_def.fixed) { \
637 builder_.PushElement(atot<CTYPE>(value.constant.c_str())); \
638 } else { \
639 builder_.AddElement(value.offset, \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800640 atot<CTYPE>( value.constant.c_str()), \
641 atot<CTYPE>(field->value.constant.c_str())); \
Wouter van Oortmerssenbe3c8742014-08-11 17:38:54 -0700642 } \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800643 break;
644 FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
645 #undef FLATBUFFERS_TD
rw48dfc692014-12-16 00:32:11 -0800646 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \
647 PTYPE) \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800648 case BASE_TYPE_ ## ENUM: \
649 builder_.Pad(field->padding); \
650 if (IsStruct(field->value.type)) { \
651 SerializeStruct(*field->value.type.struct_def, value); \
652 } else { \
653 builder_.AddOffset(value.offset, \
654 atot<CTYPE>(value.constant.c_str())); \
655 } \
656 break;
657 FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD);
658 #undef FLATBUFFERS_TD
659 }
660 }
661 }
662 }
663 for (size_t i = 0; i < fieldn; i++) field_stack_.pop_back();
664
665 if (struct_def.fixed) {
666 builder_.ClearOffsets();
667 builder_.EndStruct();
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700668 assert(value);
669 // Temporarily store this struct in the value string, since it is to
670 // be serialized in-place elsewhere.
671 value->assign(
672 reinterpret_cast<const char *>(builder_.GetCurrentBufferPointer()),
673 struct_def.bytesize);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800674 builder_.PopBytes(struct_def.bytesize);
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700675 return 0xFFFFFFFF; // Value not used by the caller.
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800676 } else {
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700677 auto off = builder_.EndTable(
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800678 start,
679 static_cast<voffset_t>(struct_def.fields.vec.size()));
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700680 if (value) *value = NumToString(off);
681 return off;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800682 }
683}
684
685uoffset_t Parser::ParseVector(const Type &type) {
686 int count = 0;
Wouter van Oortmerssen6c2dc412015-01-16 16:57:04 -0800687 for (;;) {
688 if ((!strict_json_ || !count) && IsNext(']')) break;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800689 Value val;
690 val.type = type;
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -0700691 ParseAnyValue(val, nullptr, 0);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800692 field_stack_.push_back(std::make_pair(val, nullptr));
693 count++;
Wouter van Oortmerssen6c2dc412015-01-16 16:57:04 -0800694 if (IsNext(']')) break;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800695 Expect(',');
696 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800697
Wouter van Oortmerssenbe3c8742014-08-11 17:38:54 -0700698 builder_.StartVector(count * InlineSize(type) / InlineAlignment(type),
699 InlineAlignment(type));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800700 for (int i = 0; i < count; i++) {
701 // start at the back, since we're building the data backwards.
702 auto &val = field_stack_.back().first;
703 switch (val.type.base_type) {
rw48dfc692014-12-16 00:32:11 -0800704 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800705 case BASE_TYPE_ ## ENUM: \
706 if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \
707 else builder_.PushElement(atot<CTYPE>(val.constant.c_str())); \
708 break;
709 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
710 #undef FLATBUFFERS_TD
711 }
712 field_stack_.pop_back();
713 }
714
715 builder_.ClearOffsets();
716 return builder_.EndVector(count);
717}
718
719void Parser::ParseMetaData(Definition &def) {
720 if (IsNext('(')) {
721 for (;;) {
722 auto name = attribute_;
723 Expect(kTokenIdentifier);
Wouter van Oortmerssen09521432014-11-17 17:27:26 -0800724 if (known_attributes_.find(name) == known_attributes_.end())
725 Error("user define attributes must be declared before use: " + name);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800726 auto e = new Value();
727 def.attributes.Add(name, e);
728 if (IsNext(':')) {
729 ParseSingleValue(*e);
730 }
731 if (IsNext(')')) break;
732 Expect(',');
733 }
734 }
735}
736
737bool Parser::TryTypedValue(int dtoken,
738 bool check,
739 Value &e,
740 BaseType req) {
741 bool match = dtoken == token_;
742 if (match) {
743 e.constant = attribute_;
744 if (!check) {
745 if (e.type.base_type == BASE_TYPE_NONE) {
746 e.type.base_type = req;
747 } else {
748 Error(std::string("type mismatch: expecting: ") +
749 kTypeNames[e.type.base_type] +
750 ", found: " +
751 kTypeNames[req]);
752 }
753 }
754 Next();
755 }
756 return match;
757}
758
Wouter van Oortmerssen9c3de1e2014-07-25 15:04:35 -0700759int64_t Parser::ParseIntegerFromString(Type &type) {
760 int64_t result = 0;
761 // Parse one or more enum identifiers, separated by spaces.
762 const char *next = attribute_.c_str();
763 do {
764 const char *divider = strchr(next, ' ');
765 std::string word;
766 if (divider) {
767 word = std::string(next, divider);
768 next = divider + strspn(divider, " ");
769 } else {
770 word = next;
771 next += word.length();
772 }
773 if (type.enum_def) { // The field has an enum type
774 auto enum_val = type.enum_def->vals.Lookup(word);
775 if (!enum_val)
776 Error("unknown enum value: " + word +
777 ", for enum: " + type.enum_def->name);
778 result |= enum_val->value;
779 } else { // No enum type, probably integral field.
780 if (!IsInteger(type.base_type))
781 Error("not a valid value for this field: " + word);
782 // TODO: could check if its a valid number constant here.
Wouter van Oortmerssen39833d72015-05-08 15:04:53 -0700783 const char *dot = strrchr(word.c_str(), '.');
Wouter van Oortmerssen9c3de1e2014-07-25 15:04:35 -0700784 if (!dot) Error("enum values need to be qualified by an enum type");
785 std::string enum_def_str(word.c_str(), dot);
786 std::string enum_val_str(dot + 1, word.c_str() + word.length());
Wouter van Oortmerssen39833d72015-05-08 15:04:53 -0700787 auto enum_def = LookupEnum(enum_def_str);
Wouter van Oortmerssen9c3de1e2014-07-25 15:04:35 -0700788 if (!enum_def) Error("unknown enum: " + enum_def_str);
789 auto enum_val = enum_def->vals.Lookup(enum_val_str);
790 if (!enum_val) Error("unknown enum value: " + enum_val_str);
791 result |= enum_val->value;
792 }
793 } while(*next);
794 return result;
795}
796
Alex Amesd5753212015-02-13 15:58:29 -0800797
798void Parser::ParseHash(Value &e, FieldDef* field) {
799 assert(field);
800 Value *hash_name = field->attributes.Lookup("hash");
801 switch (e.type.base_type) {
802 case BASE_TYPE_INT:
803 case BASE_TYPE_UINT: {
804 auto hash = FindHashFunction32(hash_name->constant.c_str());
805 uint32_t hashed_value = hash(attribute_.c_str());
806 e.constant = NumToString(hashed_value);
807 break;
808 }
809 case BASE_TYPE_LONG:
810 case BASE_TYPE_ULONG: {
811 auto hash = FindHashFunction64(hash_name->constant.c_str());
812 uint64_t hashed_value = hash(attribute_.c_str());
813 e.constant = NumToString(hashed_value);
814 break;
815 }
816 default:
817 assert(0);
818 }
819 Next();
820}
821
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800822void Parser::ParseSingleValue(Value &e) {
Wouter van Oortmerssen9c3de1e2014-07-25 15:04:35 -0700823 // First check if this could be a string/identifier enum value:
824 if (e.type.base_type != BASE_TYPE_STRING &&
825 e.type.base_type != BASE_TYPE_NONE &&
826 (token_ == kTokenIdentifier || token_ == kTokenStringConstant)) {
827 e.constant = NumToString(ParseIntegerFromString(e.type));
828 Next();
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700829 } else if (TryTypedValue(kTokenIntegerConstant,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800830 IsScalar(e.type.base_type),
831 e,
832 BASE_TYPE_INT) ||
833 TryTypedValue(kTokenFloatConstant,
834 IsFloat(e.type.base_type),
835 e,
836 BASE_TYPE_FLOAT) ||
837 TryTypedValue(kTokenStringConstant,
838 e.type.base_type == BASE_TYPE_STRING,
839 e,
840 BASE_TYPE_STRING)) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800841 } else {
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700842 Error("cannot parse value starting with: " + TokenToStringId(token_));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800843 }
844}
845
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700846StructDef *Parser::LookupCreateStruct(const std::string &name,
847 bool create_if_new, bool definition) {
848 std::string qualified_name = namespaces_.back()->GetFullyQualifiedName(name);
849 auto struct_def = structs_.Lookup(name);
850 if (struct_def && struct_def->predecl) {
851 if (definition) {
852 struct_def->defined_namespace = namespaces_.back();
853 structs_.Move(name, qualified_name);
854 }
855 return struct_def;
856 }
857 struct_def = structs_.Lookup(qualified_name);
858 if (!definition) {
859 // Search thru parent namespaces.
860 for (size_t components = namespaces_.back()->components.size();
861 components && !struct_def; components--) {
862 struct_def = structs_.Lookup(
863 namespaces_.back()->GetFullyQualifiedName(name, components - 1));
864 }
865 }
866 if (!struct_def && create_if_new) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800867 struct_def = new StructDef();
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700868 if (definition) {
869 structs_.Add(qualified_name, struct_def);
870 struct_def->name = name;
871 struct_def->defined_namespace = namespaces_.back();
872 } else {
873 // Not a definition.
874 // Rather than failing, we create a "pre declared" StructDef, due to
875 // circular references, and check for errors at the end of parsing.
876 // It is defined in the root namespace, since we don't know what the
877 // final namespace will be.
878 // TODO: maybe safer to use special namespace?
879 structs_.Add(name, struct_def);
880 struct_def->name = name;
881 struct_def->defined_namespace = new Namespace();
882 namespaces_.insert(namespaces_.begin(), struct_def->defined_namespace);
883 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800884 }
885 return struct_def;
886}
887
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700888EnumDef &Parser::ParseEnum(bool is_union) {
Max Galkinc3807fa2015-03-07 08:49:55 -0800889 std::vector<std::string> enum_comment = doc_comment_;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800890 Next();
Max Galkinc3807fa2015-03-07 08:49:55 -0800891 std::string enum_name = attribute_;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800892 Expect(kTokenIdentifier);
893 auto &enum_def = *new EnumDef();
Max Galkinc3807fa2015-03-07 08:49:55 -0800894 enum_def.name = enum_name;
Gabriel Martinezdf4909e2014-11-04 10:00:56 -0800895 if (!files_being_parsed_.empty()) enum_def.file = files_being_parsed_.top();
Max Galkinc3807fa2015-03-07 08:49:55 -0800896 enum_def.doc_comment = enum_comment;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800897 enum_def.is_union = is_union;
Wouter van Oortmerssen7b805352014-09-23 11:55:42 -0700898 enum_def.defined_namespace = namespaces_.back();
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700899 if (enums_.Add(namespaces_.back()->GetFullyQualifiedName(enum_name),
900 &enum_def))
Wouter van Oortmerssen39833d72015-05-08 15:04:53 -0700901 Error("enum already exists: " + enum_name);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800902 if (is_union) {
903 enum_def.underlying_type.base_type = BASE_TYPE_UTYPE;
904 enum_def.underlying_type.enum_def = &enum_def;
Wouter van Oortmerssena5f50012014-07-02 12:01:21 -0700905 } else {
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -0700906 if (proto_mode_) {
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700907 enum_def.underlying_type.base_type = BASE_TYPE_INT;
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -0700908 } else {
909 // Give specialized error message, since this type spec used to
910 // be optional in the first FlatBuffers release.
911 if (!IsNext(':')) Error("must specify the underlying integer type for this"
912 " enum (e.g. \': short\', which was the default).");
913 // Specify the integer type underlying this enum.
914 ParseType(enum_def.underlying_type);
915 if (!IsInteger(enum_def.underlying_type.base_type))
916 Error("underlying enum type must be integral");
917 }
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700918 // Make this type refer back to the enum it was derived from.
919 enum_def.underlying_type.enum_def = &enum_def;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800920 }
921 ParseMetaData(enum_def);
922 Expect('{');
923 if (is_union) enum_def.vals.Add("NONE", new EnumVal("NONE", 0));
924 do {
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700925 if (proto_mode_ && attribute_ == "option") {
926 ParseProtoOption();
927 } else {
928 auto value_name = attribute_;
929 auto full_name = value_name;
930 std::vector<std::string> value_comment = doc_comment_;
931 Expect(kTokenIdentifier);
932 if (is_union) ParseNamespacing(&full_name, &value_name);
933 auto prevsize = enum_def.vals.vec.size();
934 auto value = enum_def.vals.vec.size()
935 ? enum_def.vals.vec.back()->value + 1
936 : 0;
937 auto &ev = *new EnumVal(value_name, value);
938 if (enum_def.vals.Add(value_name, &ev))
939 Error("enum value already exists: " + value_name);
940 ev.doc_comment = value_comment;
941 if (is_union) {
942 ev.struct_def = LookupCreateStruct(full_name);
943 }
944 if (IsNext('=')) {
945 ev.value = atoi(attribute_.c_str());
946 Expect(kTokenIntegerConstant);
947 if (!proto_mode_ && prevsize &&
948 enum_def.vals.vec[prevsize - 1]->value >= ev.value)
949 Error("enum values must be specified in ascending order");
950 }
951 if (proto_mode_ && IsNext('[')) {
952 // ignore attributes on enums.
953 while (token_ != ']') Next();
954 Next();
955 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800956 }
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -0700957 } while (IsNext(proto_mode_ ? ';' : ',') && token_ != '}');
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800958 Expect('}');
Wouter van Oortmerssen127d3502014-07-17 15:12:37 -0700959 if (enum_def.attributes.Lookup("bit_flags")) {
960 for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
961 ++it) {
962 if (static_cast<size_t>((*it)->value) >=
963 SizeOf(enum_def.underlying_type.base_type) * 8)
964 Error("bit flag out of range of underlying integral type");
Wouter van Oortmerssen9c3de1e2014-07-25 15:04:35 -0700965 (*it)->value = 1LL << (*it)->value;
Wouter van Oortmerssen127d3502014-07-17 15:12:37 -0700966 }
967 }
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700968 return enum_def;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800969}
970
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700971StructDef &Parser::StartStruct(const std::string &name) {
972 auto &struct_def = *LookupCreateStruct(name, true, true);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800973 if (!struct_def.predecl) Error("datatype already exists: " + name);
974 struct_def.predecl = false;
975 struct_def.name = name;
Gabriel Martinezdf4909e2014-11-04 10:00:56 -0800976 if (!files_being_parsed_.empty()) struct_def.file = files_being_parsed_.top();
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800977 // Move this struct to the back of the vector just in case it was predeclared,
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -0700978 // to preserve declaration order.
Wouter van Oortmerssen3b070312015-05-20 14:31:02 -0700979 *remove(structs_.vec.begin(), structs_.vec.end(), &struct_def) = &struct_def;
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -0700980 return struct_def;
981}
982
983void Parser::ParseDecl() {
984 std::vector<std::string> dc = doc_comment_;
985 bool fixed = IsNext(kTokenStruct);
986 if (!fixed) Expect(kTokenTable);
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -0700987 std::string name = attribute_;
988 Expect(kTokenIdentifier);
989 auto &struct_def = StartStruct(name);
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -0700990 struct_def.doc_comment = dc;
991 struct_def.fixed = fixed;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800992 ParseMetaData(struct_def);
993 struct_def.sortbysize =
994 struct_def.attributes.Lookup("original_order") == nullptr && !fixed;
995 Expect('{');
996 while (token_ != '}') ParseField(struct_def);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800997 auto force_align = struct_def.attributes.Lookup("force_align");
998 if (fixed && force_align) {
999 auto align = static_cast<size_t>(atoi(force_align->constant.c_str()));
1000 if (force_align->type.base_type != BASE_TYPE_INT ||
1001 align < struct_def.minalign ||
Wouter van Oortmerssen45cc5032015-08-05 13:56:31 -07001002 align > 16 ||
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001003 align & (align - 1))
1004 Error("force_align must be a power of two integer ranging from the"
Wouter van Oortmerssen45cc5032015-08-05 13:56:31 -07001005 "struct\'s natural alignment to 16");
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001006 struct_def.minalign = align;
1007 }
Wouter van Oortmerssen65cfa182014-06-23 11:34:19 -07001008 struct_def.PadLastField(struct_def.minalign);
Wouter van Oortmerssen91401442014-07-07 17:34:23 -07001009 // Check if this is a table that has manual id assignments
1010 auto &fields = struct_def.fields.vec;
1011 if (!struct_def.fixed && fields.size()) {
Wouter van Oortmerssen7fcbe722014-07-08 16:35:14 -07001012 size_t num_id_fields = 0;
Wouter van Oortmerssen91401442014-07-07 17:34:23 -07001013 for (auto it = fields.begin(); it != fields.end(); ++it) {
1014 if ((*it)->attributes.Lookup("id")) num_id_fields++;
1015 }
1016 // If any fields have ids..
1017 if (num_id_fields) {
1018 // Then all fields must have them.
1019 if (num_id_fields != fields.size())
1020 Error("either all fields or no fields must have an 'id' attribute");
1021 // Simply sort by id, then the fields are the same as if no ids had
1022 // been specified.
1023 std::sort(fields.begin(), fields.end(),
1024 [](const FieldDef *a, const FieldDef *b) -> bool {
1025 auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str());
1026 auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str());
1027 return a_id < b_id;
1028 });
1029 // Verify we have a contiguous set, and reassign vtable offsets.
1030 for (int i = 0; i < static_cast<int>(fields.size()); i++) {
1031 if (i != atoi(fields[i]->attributes.Lookup("id")->constant.c_str()))
1032 Error("field id\'s must be consecutive from 0, id " +
1033 NumToString(i) + " missing or set twice");
1034 fields[i]->value.offset = FieldIndexToOffset(static_cast<voffset_t>(i));
1035 }
1036 }
1037 }
Wouter van Oortmerssen541b0672014-08-21 15:02:15 -07001038 // Check that no identifiers clash with auto generated fields.
1039 // This is not an ideal situation, but should occur very infrequently,
1040 // and allows us to keep using very readable names for type & length fields
1041 // without inducing compile errors.
1042 auto CheckClash = [&fields, &struct_def](const char *suffix,
1043 BaseType basetype) {
1044 auto len = strlen(suffix);
1045 for (auto it = fields.begin(); it != fields.end(); ++it) {
Wouter van Oortmerssenace30452015-10-19 16:05:39 -07001046 auto &fname = (*it)->name;
1047 if (fname.length() > len &&
1048 fname.compare(fname.length() - len, len, suffix) == 0 &&
Wouter van Oortmerssen541b0672014-08-21 15:02:15 -07001049 (*it)->value.type.base_type != BASE_TYPE_UTYPE) {
1050 auto field = struct_def.fields.Lookup(
Wouter van Oortmerssenace30452015-10-19 16:05:39 -07001051 fname.substr(0, fname.length() - len));
Wouter van Oortmerssen541b0672014-08-21 15:02:15 -07001052 if (field && field->value.type.base_type == basetype)
Wouter van Oortmerssenace30452015-10-19 16:05:39 -07001053 Error("Field " + fname +
Wouter van Oortmerssen541b0672014-08-21 15:02:15 -07001054 " would clash with generated functions for field " +
1055 field->name);
1056 }
1057 }
1058 };
1059 CheckClash("_type", BASE_TYPE_UNION);
1060 CheckClash("Type", BASE_TYPE_UNION);
1061 CheckClash("_length", BASE_TYPE_VECTOR);
1062 CheckClash("Length", BASE_TYPE_VECTOR);
rw5d684932015-05-09 15:37:13 -07001063 CheckClash("_byte_vector", BASE_TYPE_STRING);
1064 CheckClash("ByteVector", BASE_TYPE_STRING);
Wouter van Oortmerssen65cfa182014-06-23 11:34:19 -07001065 Expect('}');
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001066}
1067
1068bool Parser::SetRootType(const char *name) {
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001069 root_struct_def_ = structs_.Lookup(
1070 namespaces_.back()->GetFullyQualifiedName(name));
Wouter van Oortmerssen81312c22015-05-21 16:33:29 -07001071 return root_struct_def_ != nullptr;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001072}
1073
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -07001074void Parser::MarkGenerated() {
1075 // Since the Parser object retains definitions across files, we must
1076 // ensure we only output code for definitions once, in the file they are first
1077 // declared. This function marks all existing definitions as having already
1078 // been generated.
1079 for (auto it = enums_.vec.begin();
1080 it != enums_.vec.end(); ++it) {
1081 (*it)->generated = true;
1082 }
1083 for (auto it = structs_.vec.begin();
1084 it != structs_.vec.end(); ++it) {
1085 (*it)->generated = true;
1086 }
1087}
1088
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001089void Parser::ParseNamespace() {
1090 Next();
1091 auto ns = new Namespace();
1092 namespaces_.push_back(ns);
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001093 if (token_ != ';') {
1094 for (;;) {
1095 ns->components.push_back(attribute_);
1096 Expect(kTokenIdentifier);
1097 if (!IsNext('.')) break;
1098 }
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001099 }
1100 Expect(';');
1101}
1102
1103// Best effort parsing of .proto declarations, with the aim to turn them
1104// in the closest corresponding FlatBuffer equivalent.
1105// We parse everything as identifiers instead of keywords, since we don't
1106// want protobuf keywords to become invalid identifiers in FlatBuffers.
1107void Parser::ParseProtoDecl() {
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001108 bool isextend = attribute_ == "extend";
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001109 if (attribute_ == "package") {
1110 // These are identical in syntax to FlatBuffer's namespace decl.
1111 ParseNamespace();
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001112 } else if (attribute_ == "message" || isextend) {
Advay Mengle3ad85362015-03-31 02:03:11 -07001113 std::vector<std::string> struct_comment = doc_comment_;
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001114 Next();
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001115 StructDef *struct_def = nullptr;
1116 if (isextend) {
1117 IsNext('.'); // qualified names may start with a . ?
1118 auto id = attribute_;
1119 Expect(kTokenIdentifier);
1120 ParseNamespacing(&id, nullptr);
1121 struct_def = LookupCreateStruct(id, false);
1122 if (!struct_def) Error("cannot extend unknown message type: " + id);
1123 } else {
1124 std::string name = attribute_;
1125 Expect(kTokenIdentifier);
1126 struct_def = &StartStruct(name);
1127 // Since message definitions can be nested, we create a new namespace.
1128 auto ns = new Namespace();
1129 // Copy of current namespace.
1130 *ns = *namespaces_.back();
1131 // But with current message name.
1132 ns->components.push_back(name);
1133 namespaces_.push_back(ns);
1134 }
1135 struct_def->doc_comment = struct_comment;
1136 ParseProtoFields(struct_def, isextend, false);
1137 if (!isextend) {
1138 // We have to remove the nested namespace, but we can't just throw it
1139 // away, so put it at the beginning of the vector.
1140 auto ns = namespaces_.back();
1141 namespaces_.pop_back();
1142 namespaces_.insert(namespaces_.begin(), ns);
1143 }
1144 IsNext(';');
1145 } else if (attribute_ == "enum") {
1146 // These are almost the same, just with different terminator:
1147 auto &enum_def = ParseEnum(false);
1148 IsNext(';');
1149 // Protobuf allows them to be specified in any order, so sort afterwards.
1150 auto &v = enum_def.vals.vec;
1151 std::sort(v.begin(), v.end(), [](const EnumVal *a, const EnumVal *b) {
1152 return a->value < b->value;
1153 });
1154 // Temp: remove any duplicates, as .fbs files can't handle them.
1155 for (auto it = v.begin(); it != v.end(); ) {
1156 if (it != v.begin() && it[0]->value == it[-1]->value) it = v.erase(it);
1157 else ++it;
1158 }
1159 } else if (attribute_ == "syntax") { // Skip these.
1160 Next();
1161 Expect('=');
1162 Expect(kTokenStringConstant);
1163 Expect(';');
1164 } else if (attribute_ == "option") { // Skip these.
1165 ParseProtoOption();
1166 Expect(';');
1167 } else if (attribute_ == "service") { // Skip these.
1168 Next();
1169 Expect(kTokenIdentifier);
1170 ParseProtoCurliesOrIdent();
1171 } else {
1172 Error("don\'t know how to parse .proto declaration starting with " +
1173 TokenToStringId(token_));
1174 }
1175}
1176
1177void Parser::ParseProtoFields(StructDef *struct_def, bool isextend,
1178 bool inside_oneof) {
1179 Expect('{');
1180 while (token_ != '}') {
1181 if (attribute_ == "message" || attribute_ == "extend" ||
1182 attribute_ == "enum") {
1183 // Nested declarations.
1184 ParseProtoDecl();
1185 } else if (attribute_ == "extensions") { // Skip these.
1186 Next();
1187 Expect(kTokenIntegerConstant);
1188 if (IsNext(kTokenIdentifier)) { // to
1189 Next(); // num
1190 }
1191 Expect(';');
1192 } else if (attribute_ == "option") { // Skip these.
1193 ParseProtoOption();
1194 Expect(';');
1195 } else if (attribute_ == "reserved") { // Skip these.
1196 Next();
1197 Expect(kTokenIntegerConstant);
1198 while (IsNext(',')) Expect(kTokenIntegerConstant);
1199 Expect(';');
1200 } else {
1201 std::vector<std::string> field_comment = doc_comment_;
1202 // Parse the qualifier.
1203 bool required = false;
1204 bool repeated = false;
1205 bool oneof = false;
1206 if (!inside_oneof) {
Wouter van Oortmerssen2abe24b2015-09-28 09:42:45 -07001207 if (attribute_ == "optional") {
1208 // This is the default.
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001209 Expect(kTokenIdentifier);
Wouter van Oortmerssen2abe24b2015-09-28 09:42:45 -07001210 } else if (attribute_ == "required") {
1211 required = true;
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001212 Expect(kTokenIdentifier);
Wouter van Oortmerssen2abe24b2015-09-28 09:42:45 -07001213 } else if (attribute_ == "repeated") {
1214 repeated = true;
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001215 Expect(kTokenIdentifier);
1216 } else if (attribute_ == "oneof") {
1217 oneof = true;
1218 Expect(kTokenIdentifier);
Wouter van Oortmerssen2abe24b2015-09-28 09:42:45 -07001219 } else {
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001220 // can't error, proto3 allows decls without any of the above.
Wouter van Oortmerssen2abe24b2015-09-28 09:42:45 -07001221 }
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001222 }
1223 StructDef *anonymous_struct = nullptr;
1224 Type type;
1225 if (attribute_ == "group" || oneof) {
1226 if (!oneof) Expect(kTokenIdentifier);
1227 auto name = "Anonymous" + NumToString(anonymous_counter++);
1228 anonymous_struct = &StartStruct(name);
1229 type = Type(BASE_TYPE_STRUCT, anonymous_struct);
1230 } else {
1231 type = ParseTypeFromProtoType();
1232 }
1233 // Repeated elements get mapped to a vector.
1234 if (repeated) {
1235 type.element = type.base_type;
1236 type.base_type = BASE_TYPE_VECTOR;
1237 }
1238 std::string name = attribute_;
1239 // Protos may use our keywords "attribute" & "namespace" as an identifier.
1240 if (IsNext(kTokenAttribute) || IsNext(kTokenNameSpace)) {
1241 // TODO: simpler to just not make these keywords?
1242 name += "_"; // Have to make it not a keyword.
1243 } else {
Wouter van Oortmerssen2abe24b2015-09-28 09:42:45 -07001244 Expect(kTokenIdentifier);
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001245 }
1246 if (!oneof) {
Wouter van Oortmerssen2abe24b2015-09-28 09:42:45 -07001247 // Parse the field id. Since we're just translating schemas, not
1248 // any kind of binary compatibility, we can safely ignore these, and
1249 // assign our own.
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001250 Expect('=');
Wouter van Oortmerssen2abe24b2015-09-28 09:42:45 -07001251 Expect(kTokenIntegerConstant);
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001252 }
1253 FieldDef *existing_field = nullptr;
1254 if (isextend) {
1255 // We allow a field to be re-defined when extending.
1256 // TODO: are there situations where that is problematic?
1257 existing_field = struct_def->fields.Lookup(name);
1258 }
1259 auto &field = existing_field
1260 ? *existing_field
1261 : AddField(*struct_def, name, type);
1262 field.doc_comment = field_comment;
1263 if (!IsScalar(type.base_type)) field.required = required;
1264 // See if there's a default specified.
1265 if (IsNext('[')) {
1266 do {
1267 auto key = attribute_;
1268 ParseProtoKey();
Wouter van Oortmerssen2abe24b2015-09-28 09:42:45 -07001269 Expect('=');
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001270 auto val = attribute_;
1271 ParseProtoCurliesOrIdent();
1272 if (key == "default") {
1273 // Temp: skip non-numeric defaults (enums).
1274 auto numeric = strpbrk(val.c_str(), "0123456789-+.");
1275 if (IsScalar(type.base_type) && numeric == val.c_str())
1276 field.value.constant = val;
1277 } else if (key == "deprecated") {
1278 field.deprecated = val == "true";
1279 }
1280 } while (IsNext(','));
1281 Expect(']');
1282 }
1283 if (anonymous_struct) {
1284 ParseProtoFields(anonymous_struct, false, oneof);
1285 IsNext(';');
1286 } else {
Wouter van Oortmerssen2abe24b2015-09-28 09:42:45 -07001287 Expect(';');
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001288 }
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001289 }
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001290 }
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001291 Next();
1292}
1293
1294void Parser::ParseProtoKey() {
1295 if (token_ == '(') {
1296 Next();
1297 // Skip "(a.b)" style custom attributes.
1298 while (token_ == '.' || token_ == kTokenIdentifier) Next();
1299 Expect(')');
1300 while (IsNext('.')) Expect(kTokenIdentifier);
1301 } else {
1302 Expect(kTokenIdentifier);
1303 }
1304}
1305
1306void Parser::ParseProtoCurliesOrIdent() {
1307 if (IsNext('{')) {
1308 for (int nesting = 1; nesting; ) {
1309 if (token_ == '{') nesting++;
1310 else if (token_ == '}') nesting--;
1311 Next();
1312 }
1313 } else {
1314 Next(); // Any single token.
1315 }
1316}
1317
1318void Parser::ParseProtoOption() {
1319 Next();
1320 ParseProtoKey();
1321 Expect('=');
1322 ParseProtoCurliesOrIdent();
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001323}
1324
1325// Parse a protobuf type, and map it to the corresponding FlatBuffer one.
1326Type Parser::ParseTypeFromProtoType() {
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001327 struct type_lookup { const char *proto_type; BaseType fb_type; };
1328 static type_lookup lookup[] = {
1329 { "float", BASE_TYPE_FLOAT }, { "double", BASE_TYPE_DOUBLE },
1330 { "int32", BASE_TYPE_INT }, { "int64", BASE_TYPE_LONG },
1331 { "uint32", BASE_TYPE_UINT }, { "uint64", BASE_TYPE_ULONG },
1332 { "sint32", BASE_TYPE_INT }, { "sint64", BASE_TYPE_LONG },
1333 { "fixed32", BASE_TYPE_UINT }, { "fixed64", BASE_TYPE_ULONG },
1334 { "sfixed32", BASE_TYPE_INT }, { "sfixed64", BASE_TYPE_LONG },
1335 { "bool", BASE_TYPE_BOOL },
1336 { "string", BASE_TYPE_STRING },
1337 { "bytes", BASE_TYPE_STRING },
1338 { nullptr, BASE_TYPE_NONE }
1339 };
1340 Type type;
1341 for (auto tl = lookup; tl->proto_type; tl++) {
1342 if (attribute_ == tl->proto_type) {
1343 type.base_type = tl->fb_type;
1344 Next();
1345 return type;
1346 }
1347 }
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001348 IsNext('.'); // qualified names may start with a . ?
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001349 ParseTypeIdent(type);
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001350 return type;
1351}
1352
Wouter van Oortmerssen30642c52014-09-22 15:49:43 -07001353bool Parser::Parse(const char *source, const char **include_paths,
1354 const char *source_filename) {
Gabriel Martinezdf4909e2014-11-04 10:00:56 -08001355 if (source_filename &&
1356 included_files_.find(source_filename) == included_files_.end()) {
1357 included_files_[source_filename] = true;
1358 files_included_per_file_[source_filename] = std::set<std::string>();
1359 files_being_parsed_.push(source_filename);
1360 }
1361 if (!include_paths) {
Wouter van Oortmerssen1e6f8f52015-06-22 10:23:42 -07001362 static const char *current_directory[] = { "", nullptr };
Gabriel Martinezdf4909e2014-11-04 10:00:56 -08001363 include_paths = current_directory;
1364 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001365 source_ = cursor_ = source;
1366 line_ = 1;
1367 error_.clear();
1368 builder_.Clear();
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001369 // Start with a blank namespace just in case this file doesn't have one.
1370 namespaces_.push_back(new Namespace());
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001371 try {
1372 Next();
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001373 // Includes must come before type declarations:
1374 for (;;) {
1375 // Parse pre-include proto statements if any:
1376 if (proto_mode_ &&
1377 (attribute_ == "option" || attribute_ == "syntax" ||
1378 attribute_ == "package")) {
1379 ParseProtoDecl();
1380 } else if (IsNext(kTokenInclude) ||
1381 (proto_mode_ &&
1382 attribute_ == "import" &&
1383 IsNext(kTokenIdentifier))) {
1384 if (proto_mode_ && attribute_ == "public") Next();
1385 auto name = attribute_;
1386 Expect(kTokenStringConstant);
1387 // Look for the file in include_paths.
1388 std::string filepath;
1389 for (auto paths = include_paths; paths && *paths; paths++) {
1390 filepath = flatbuffers::ConCatPathFileName(*paths, name);
1391 if(FileExists(filepath.c_str())) break;
Wouter van Oortmerssene57b86b2014-09-11 17:13:21 -07001392 }
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001393 if (filepath.empty())
1394 Error("unable to locate include file: " + name);
1395 if (source_filename)
1396 files_included_per_file_[source_filename].insert(filepath);
1397 if (included_files_.find(filepath) == included_files_.end()) {
1398 // We found an include file that we have not parsed yet.
1399 // Load it and parse it.
1400 std::string contents;
1401 if (!LoadFile(filepath.c_str(), true, &contents))
1402 Error("unable to load include file: " + name);
1403 if (!Parse(contents.c_str(), include_paths, filepath.c_str())) {
1404 // Any errors, we're done.
1405 return false;
1406 }
1407 // We do not want to output code for any included files:
1408 MarkGenerated();
1409 // This is the easiest way to continue this file after an include:
1410 // instead of saving and restoring all the state, we simply start the
1411 // file anew. This will cause it to encounter the same include statement
1412 // again, but this time it will skip it, because it was entered into
1413 // included_files_.
1414 // This is recursive, but only go as deep as the number of include
1415 // statements.
1416 return Parse(source, include_paths, source_filename);
1417 }
1418 Expect(';');
1419 } else {
1420 break;
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -07001421 }
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -07001422 }
1423 // Now parse all other kinds of declarations:
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001424 while (token_ != kTokenEof) {
Wouter van Oortmerssend38b9af2014-09-26 16:46:30 -07001425 if (proto_mode_) {
1426 ParseProtoDecl();
1427 } else if (token_ == kTokenNameSpace) {
1428 ParseNamespace();
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001429 } else if (token_ == '{') {
Wouter van Oortmerssen81312c22015-05-21 16:33:29 -07001430 if (!root_struct_def_) Error("no root type set to parse json with");
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001431 if (builder_.GetSize()) {
1432 Error("cannot have more than one json object in a file");
1433 }
Wouter van Oortmerssen4d781042015-09-28 15:02:41 -07001434 builder_.Finish(Offset<Table>(ParseTable(*root_struct_def_, nullptr)),
Wouter van Oortmerssen09a29992014-09-04 16:31:44 -07001435 file_identifier_.length() ? file_identifier_.c_str() : nullptr);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001436 } else if (token_ == kTokenEnum) {
1437 ParseEnum(false);
1438 } else if (token_ == kTokenUnion) {
1439 ParseEnum(true);
1440 } else if (token_ == kTokenRootType) {
1441 Next();
1442 auto root_type = attribute_;
1443 Expect(kTokenIdentifier);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001444 if (!SetRootType(root_type.c_str()))
1445 Error("unknown root type: " + root_type);
Wouter van Oortmerssen81312c22015-05-21 16:33:29 -07001446 if (root_struct_def_->fixed)
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001447 Error("root type must be a table");
Wouter van Oortmerssen5da7bda2014-07-31 15:11:03 -07001448 Expect(';');
1449 } else if (token_ == kTokenFileIdentifier) {
1450 Next();
1451 file_identifier_ = attribute_;
1452 Expect(kTokenStringConstant);
1453 if (file_identifier_.length() !=
1454 FlatBufferBuilder::kFileIdentifierLength)
1455 Error("file_identifier must be exactly " +
1456 NumToString(FlatBufferBuilder::kFileIdentifierLength) +
1457 " characters");
1458 Expect(';');
1459 } else if (token_ == kTokenFileExtension) {
1460 Next();
1461 file_extension_ = attribute_;
1462 Expect(kTokenStringConstant);
1463 Expect(';');
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -07001464 } else if(token_ == kTokenInclude) {
1465 Error("includes must come before declarations");
Wouter van Oortmerssen09521432014-11-17 17:27:26 -08001466 } else if(token_ == kTokenAttribute) {
1467 Next();
1468 auto name = attribute_;
1469 Expect(kTokenStringConstant);
1470 Expect(';');
1471 known_attributes_.insert(name);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001472 } else {
1473 ParseDecl();
1474 }
1475 }
1476 for (auto it = structs_.vec.begin(); it != structs_.vec.end(); ++it) {
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001477 if ((*it)->predecl) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001478 Error("type referenced but not defined: " + (*it)->name);
Wouter van Oortmerssen94680f52015-10-05 17:39:08 -07001479 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001480 }
1481 for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
1482 auto &enum_def = **it;
1483 if (enum_def.is_union) {
Max Galkinc3807fa2015-03-07 08:49:55 -08001484 for (auto val_it = enum_def.vals.vec.begin();
1485 val_it != enum_def.vals.vec.end();
1486 ++val_it) {
1487 auto &val = **val_it;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001488 if (val.struct_def && val.struct_def->fixed)
1489 Error("only tables can be union elements: " + val.name);
1490 }
1491 }
1492 }
1493 } catch (const std::string &msg) {
Wouter van Oortmerssen85c9c832014-09-22 17:17:13 -07001494 error_ = source_filename ? AbsolutePath(source_filename) : "";
1495 #ifdef _WIN32
1496 error_ += "(" + NumToString(line_) + ")"; // MSVC alike
1497 #else
1498 if (source_filename) error_ += ":";
1499 error_ += NumToString(line_) + ":0"; // gcc alike
1500 #endif
1501 error_ += ": error: " + msg;
Gabriel Martinezdf4909e2014-11-04 10:00:56 -08001502 if (source_filename) files_being_parsed_.pop();
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001503 return false;
1504 }
Gabriel Martinezdf4909e2014-11-04 10:00:56 -08001505 if (source_filename) files_being_parsed_.pop();
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001506 return true;
1507}
1508
Gabriel Martinezdf4909e2014-11-04 10:00:56 -08001509std::set<std::string> Parser::GetIncludedFilesRecursive(
1510 const std::string &file_name) const {
1511 std::set<std::string> included_files;
1512 std::list<std::string> to_process;
1513
1514 if (file_name.empty()) return included_files;
1515 to_process.push_back(file_name);
1516
1517 while (!to_process.empty()) {
1518 std::string current = to_process.front();
1519 to_process.pop_front();
1520 included_files.insert(current);
1521
1522 auto new_files = files_included_per_file_.at(current);
1523 for (auto it = new_files.begin(); it != new_files.end(); ++it) {
1524 if (included_files.find(*it) == included_files.end())
1525 to_process.push_back(*it);
1526 }
1527 }
1528
1529 return included_files;
1530}
1531
Wouter van Oortmerssen81312c22015-05-21 16:33:29 -07001532// Schema serialization functionality:
1533
1534template<typename T> void AssignIndices(const std::vector<T *> &defvec) {
1535 // Pre-sort these vectors, such that we can set the correct indices for them.
1536 auto vec = defvec;
1537 std::sort(vec.begin(), vec.end(),
1538 [](const T *a, const T *b) { return a->name < b->name; });
1539 for (int i = 0; i < static_cast<int>(vec.size()); i++) vec[i]->index = i;
1540}
1541
1542void Parser::Serialize() {
1543 builder_.Clear();
1544 AssignIndices(structs_.vec);
1545 AssignIndices(enums_.vec);
1546 std::vector<Offset<reflection::Object>> object_offsets;
1547 for (auto it = structs_.vec.begin(); it != structs_.vec.end(); ++it) {
1548 auto offset = (*it)->Serialize(&builder_);
1549 object_offsets.push_back(offset);
1550 (*it)->serialized_location = offset.o;
1551 }
1552 std::vector<Offset<reflection::Enum>> enum_offsets;
1553 for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
1554 auto offset = (*it)->Serialize(&builder_);
1555 enum_offsets.push_back(offset);
1556 (*it)->serialized_location = offset.o;
1557 }
1558 auto schema_offset = reflection::CreateSchema(
1559 builder_,
1560 builder_.CreateVectorOfSortedTables(&object_offsets),
1561 builder_.CreateVectorOfSortedTables(&enum_offsets),
1562 builder_.CreateString(file_identifier_),
1563 builder_.CreateString(file_extension_),
Wouter van Oortmerssen36c7e9a2015-06-29 15:21:48 -07001564 root_struct_def_
1565 ? root_struct_def_->serialized_location
1566 : 0);
Wouter van Oortmerssen81312c22015-05-21 16:33:29 -07001567 builder_.Finish(schema_offset, reflection::SchemaIdentifier());
1568}
1569
1570Offset<reflection::Object> StructDef::Serialize(FlatBufferBuilder *builder)
1571 const {
1572 std::vector<Offset<reflection::Field>> field_offsets;
1573 for (auto it = fields.vec.begin(); it != fields.vec.end(); ++it) {
Wouter van Oortmerssen622b8d02015-06-15 15:57:48 -07001574 field_offsets.push_back(
1575 (*it)->Serialize(builder,
1576 static_cast<uint16_t>(it - fields.vec.begin())));
Wouter van Oortmerssen81312c22015-05-21 16:33:29 -07001577 }
1578 return reflection::CreateObject(*builder,
1579 builder->CreateString(name),
1580 builder->CreateVectorOfSortedTables(
1581 &field_offsets),
Wouter van Oortmerssencb2b2be2015-06-23 16:06:35 -07001582 fixed,
1583 static_cast<int>(minalign),
1584 static_cast<int>(bytesize));
Wouter van Oortmerssen81312c22015-05-21 16:33:29 -07001585}
1586
1587Offset<reflection::Field> FieldDef::Serialize(FlatBufferBuilder *builder,
1588 uint16_t id) const {
1589 return reflection::CreateField(*builder,
1590 builder->CreateString(name),
1591 value.type.Serialize(builder),
1592 id,
1593 value.offset,
1594 IsInteger(value.type.base_type)
1595 ? StringToInt(value.constant.c_str())
1596 : 0,
1597 IsFloat(value.type.base_type)
1598 ? strtod(value.constant.c_str(), nullptr)
1599 : 0.0,
1600 deprecated,
1601 required,
1602 key);
1603 // TODO: value.constant is almost always "0", we could save quite a bit of
1604 // space by sharing it. Same for common values of value.type.
1605}
1606
1607Offset<reflection::Enum> EnumDef::Serialize(FlatBufferBuilder *builder) const {
1608 std::vector<Offset<reflection::EnumVal>> enumval_offsets;
1609 for (auto it = vals.vec.begin(); it != vals.vec.end(); ++it) {
1610 enumval_offsets.push_back((*it)->Serialize(builder));
1611 }
1612 return reflection::CreateEnum(*builder,
1613 builder->CreateString(name),
1614 builder->CreateVector(enumval_offsets),
1615 is_union,
1616 underlying_type.Serialize(builder));
1617}
1618
1619Offset<reflection::EnumVal> EnumVal::Serialize(FlatBufferBuilder *builder) const
1620 {
1621 return reflection::CreateEnumVal(*builder,
1622 builder->CreateString(name),
1623 value,
1624 struct_def
1625 ? struct_def->serialized_location
1626 : 0);
1627}
1628
1629Offset<reflection::Type> Type::Serialize(FlatBufferBuilder *builder) const {
1630 return reflection::CreateType(*builder,
1631 static_cast<reflection::BaseType>(base_type),
1632 static_cast<reflection::BaseType>(element),
1633 struct_def ? struct_def->index :
1634 (enum_def ? enum_def->index : -1));
1635}
1636
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -08001637} // namespace flatbuffers