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