blob: ea21ec66414773153349a3785aa31ed593f2ddaa [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[] = {
rw74d5f372014-07-11 16:12:35 -070026 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) 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[] = {
rw74d5f372014-07-11 16:12:35 -070033 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) sizeof(CTYPE),
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080034 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
35 #undef FLATBUFFERS_TD
36};
37
38static void Error(const std::string &msg) {
39 throw msg;
40}
41
42// Ensure that integer values we parse fit inside the declared integer type.
43static void CheckBitsFit(int64_t val, size_t bits) {
44 auto mask = (1ll << bits) - 1; // Bits we allow to be used.
45 if (bits < 64 &&
46 (val & ~mask) != 0 && // Positive or unsigned.
47 (val | mask) != -1) // Negative.
48 Error("constant does not fit in a " + NumToString(bits) + "-bit field");
49}
50
51// atot: templated version of atoi/atof: convert a string to an instance of T.
52template<typename T> inline T atot(const char *s) {
53 auto val = StringToInt(s);
54 CheckBitsFit(val, sizeof(T) * 8);
55 return (T)val;
56}
57template<> inline bool atot<bool>(const char *s) {
58 return 0 != atoi(s);
59}
60template<> inline float atot<float>(const char *s) {
61 return static_cast<float>(strtod(s, nullptr));
62}
63template<> inline double atot<double>(const char *s) {
64 return strtod(s, nullptr);
65}
66
67template<> inline Offset<void> atot<Offset<void>>(const char *s) {
68 return Offset<void>(atoi(s));
69}
70
71// Declare tokens we'll use. Single character tokens are represented by their
72// ascii character code (e.g. '{'), others above 256.
73#define FLATBUFFERS_GEN_TOKENS(TD) \
74 TD(Eof, 256, "end of file") \
75 TD(StringConstant, 257, "string constant") \
76 TD(IntegerConstant, 258, "integer constant") \
77 TD(FloatConstant, 259, "float constant") \
78 TD(Identifier, 260, "identifier") \
79 TD(Table, 261, "table") \
80 TD(Struct, 262, "struct") \
81 TD(Enum, 263, "enum") \
82 TD(Union, 264, "union") \
83 TD(NameSpace, 265, "namespace") \
Wouter van Oortmerssen5da7bda2014-07-31 15:11:03 -070084 TD(RootType, 266, "root_type") \
85 TD(FileIdentifier, 267, "file_identifier") \
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -070086 TD(FileExtension, 268, "file_extension") \
87 TD(Include, 269, "include")
Wouter van Oortmerssen8f80fec2014-07-29 10:29:38 -070088#ifdef __GNUC__
89__extension__ // Stop GCC complaining about trailing comma with -Wpendantic.
90#endif
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080091enum {
Wouter van Oortmerssen75349ae2014-07-09 11:44:26 -070092 #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) kToken ## NAME = VALUE,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080093 FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
94 #undef FLATBUFFERS_TOKEN
rw74d5f372014-07-11 16:12:35 -070095 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) kToken ## ENUM,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080096 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
97 #undef FLATBUFFERS_TD
98};
99
100static std::string TokenToString(int t) {
101 static const char *tokens[] = {
102 #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) STRING,
103 FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
104 #undef FLATBUFFERS_TOKEN
rw74d5f372014-07-11 16:12:35 -0700105 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) IDLTYPE,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800106 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
107 #undef FLATBUFFERS_TD
108 };
109 if (t < 256) { // A single ascii char token.
110 std::string s;
Wouter van Oortmerssen8e409022014-09-02 17:32:12 -0700111 s.append(1, static_cast<char>(t));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800112 return s;
113 } else { // Other tokens.
114 return tokens[t - 256];
115 }
116}
117
Wouter van Oortmerssenebac1e12014-08-20 13:22:16 -0700118// Parses exactly nibbles worth of hex digits into a number, or error.
119int64_t Parser::ParseHexNum(int nibbles) {
120 for (int i = 0; i < nibbles; i++)
121 if (!isxdigit(cursor_[i]))
122 Error("escape code must be followed by " + NumToString(nibbles) +
123 " hex digits");
124 auto val = StringToInt(cursor_, 16);
125 cursor_ += nibbles;
126 return val;
127}
128
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800129void Parser::Next() {
130 doc_comment_.clear();
131 bool seen_newline = false;
132 for (;;) {
133 char c = *cursor_++;
134 token_ = c;
135 switch (c) {
136 case '\0': cursor_--; token_ = kTokenEof; return;
137 case ' ': case '\r': case '\t': break;
138 case '\n': line_++; seen_newline = true; break;
139 case '{': case '}': case '(': case ')': case '[': case ']': return;
140 case ',': case ':': case ';': case '=': return;
141 case '.':
142 if(!isdigit(*cursor_)) return;
143 Error("floating point constant can\'t start with \".\"");
144 break;
145 case '\"':
146 attribute_ = "";
147 while (*cursor_ != '\"') {
148 if (*cursor_ < ' ' && *cursor_ >= 0)
149 Error("illegal character in string constant");
150 if (*cursor_ == '\\') {
151 cursor_++;
152 switch (*cursor_) {
153 case 'n': attribute_ += '\n'; cursor_++; break;
154 case 't': attribute_ += '\t'; cursor_++; break;
155 case 'r': attribute_ += '\r'; cursor_++; break;
Wouter van Oortmerssenebac1e12014-08-20 13:22:16 -0700156 case 'b': attribute_ += '\b'; cursor_++; break;
157 case 'f': attribute_ += '\f'; cursor_++; break;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800158 case '\"': attribute_ += '\"'; cursor_++; break;
159 case '\\': attribute_ += '\\'; cursor_++; break;
Wouter van Oortmerssenebac1e12014-08-20 13:22:16 -0700160 case '/': attribute_ += '/'; cursor_++; break;
161 case 'x': { // Not in the JSON standard
162 cursor_++;
163 attribute_ += static_cast<char>(ParseHexNum(2));
164 break;
165 }
166 case 'u': {
167 cursor_++;
168 ToUTF8(static_cast<int>(ParseHexNum(4)), &attribute_);
169 break;
170 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800171 default: Error("unknown escape code in string constant"); break;
172 }
173 } else { // printable chars + UTF-8 bytes
174 attribute_ += *cursor_++;
175 }
176 }
177 cursor_++;
178 token_ = kTokenStringConstant;
179 return;
180 case '/':
181 if (*cursor_ == '/') {
182 const char *start = ++cursor_;
183 while (*cursor_ && *cursor_ != '\n') cursor_++;
184 if (*start == '/') { // documentation comment
185 if (!seen_newline)
186 Error("a documentation comment should be on a line on its own");
187 // todo: do we want to support multiline comments instead?
188 doc_comment_ += std::string(start + 1, cursor_);
189 }
190 break;
191 }
192 // fall thru
193 default:
194 if (isalpha(static_cast<unsigned char>(c))) {
195 // Collect all chars of an identifier:
196 const char *start = cursor_ - 1;
197 while (isalnum(static_cast<unsigned char>(*cursor_)) ||
198 *cursor_ == '_')
199 cursor_++;
200 attribute_.clear();
201 attribute_.append(start, cursor_);
202 // First, see if it is a type keyword from the table of types:
rw74d5f372014-07-11 16:12:35 -0700203 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800204 if (attribute_ == IDLTYPE) { \
205 token_ = kToken ## ENUM; \
206 return; \
207 }
208 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
209 #undef FLATBUFFERS_TD
210 // If it's a boolean constant keyword, turn those into integers,
211 // which simplifies our logic downstream.
212 if (attribute_ == "true" || attribute_ == "false") {
213 attribute_ = NumToString(attribute_ == "true");
214 token_ = kTokenIntegerConstant;
215 return;
216 }
217 // Check for declaration keywords:
218 if (attribute_ == "table") { token_ = kTokenTable; return; }
219 if (attribute_ == "struct") { token_ = kTokenStruct; return; }
220 if (attribute_ == "enum") { token_ = kTokenEnum; return; }
221 if (attribute_ == "union") { token_ = kTokenUnion; return; }
222 if (attribute_ == "namespace") { token_ = kTokenNameSpace; return; }
223 if (attribute_ == "root_type") { token_ = kTokenRootType; return; }
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700224 if (attribute_ == "include") { token_ = kTokenInclude; return; }
Wouter van Oortmerssen5da7bda2014-07-31 15:11:03 -0700225 if (attribute_ == "file_identifier") {
226 token_ = kTokenFileIdentifier;
227 return;
228 }
229 if (attribute_ == "file_extension") {
230 token_ = kTokenFileExtension;
231 return;
232 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800233 // If not, it is a user-defined identifier:
234 token_ = kTokenIdentifier;
235 return;
236 } else if (isdigit(static_cast<unsigned char>(c)) || c == '-') {
237 const char *start = cursor_ - 1;
238 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
239 if (*cursor_ == '.') {
240 cursor_++;
241 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
Wouter van Oortmerssen93df5692014-07-10 13:40:55 -0700242 // See if this float has a scientific notation suffix. Both JSON
243 // and C++ (through strtod() we use) have the same format:
244 if (*cursor_ == 'e' || *cursor_ == 'E') {
245 cursor_++;
246 if (*cursor_ == '+' || *cursor_ == '-') cursor_++;
247 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
248 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800249 token_ = kTokenFloatConstant;
250 } else {
251 token_ = kTokenIntegerConstant;
252 }
253 attribute_.clear();
254 attribute_.append(start, cursor_);
255 return;
256 }
257 std::string ch;
258 ch = c;
259 if (c < ' ' || c > '~') ch = "code: " + NumToString(c);
260 Error("illegal character: " + ch);
261 break;
262 }
263 }
264}
265
266// Check if a given token is next, if so, consume it as well.
267bool Parser::IsNext(int t) {
268 bool isnext = t == token_;
269 if (isnext) Next();
270 return isnext;
271}
272
273// Expect a given token to be next, consume it, or error if not present.
274void Parser::Expect(int t) {
275 if (t != token_) {
276 Error("expecting: " + TokenToString(t) + " instead got: " +
277 TokenToString(token_));
278 }
279 Next();
280}
281
282// Parse any IDL type.
283void Parser::ParseType(Type &type) {
284 if (token_ >= kTokenBOOL && token_ <= kTokenSTRING) {
285 type.base_type = static_cast<BaseType>(token_ - kTokenNONE);
286 } else {
287 if (token_ == kTokenIdentifier) {
288 auto enum_def = enums_.Lookup(attribute_);
289 if (enum_def) {
290 type = enum_def->underlying_type;
291 if (enum_def->is_union) type.base_type = BASE_TYPE_UNION;
292 } else {
293 type.base_type = BASE_TYPE_STRUCT;
294 type.struct_def = LookupCreateStruct(attribute_);
295 }
296 } else if (token_ == '[') {
297 Next();
298 Type subtype;
299 ParseType(subtype);
300 if (subtype.base_type == BASE_TYPE_VECTOR) {
301 // We could support this, but it will complicate things, and it's
302 // easier to work around with a struct around the inner vector.
303 Error("nested vector types not supported (wrap in table first).");
304 }
305 if (subtype.base_type == BASE_TYPE_UNION) {
306 // We could support this if we stored a struct of 2 elements per
307 // union element.
308 Error("vector of union types not supported (wrap in table first).");
309 }
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700310 type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800311 type.element = subtype.base_type;
312 Expect(']');
313 return;
314 } else {
315 Error("illegal type syntax");
316 }
317 }
318 Next();
319}
320
321FieldDef &Parser::AddField(StructDef &struct_def,
322 const std::string &name,
323 const Type &type) {
324 auto &field = *new FieldDef();
325 field.value.offset =
326 FieldIndexToOffset(static_cast<voffset_t>(struct_def.fields.vec.size()));
327 field.name = name;
328 field.value.type = type;
329 if (struct_def.fixed) { // statically compute the field offset
330 auto size = InlineSize(type);
331 auto alignment = InlineAlignment(type);
332 // structs_ need to have a predictable format, so we need to align to
333 // the largest scalar
334 struct_def.minalign = std::max(struct_def.minalign, alignment);
335 struct_def.PadLastField(alignment);
Wouter van Oortmerssen12563072014-06-30 15:56:31 -0700336 field.value.offset = static_cast<voffset_t>(struct_def.bytesize);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800337 struct_def.bytesize += size;
338 }
339 if (struct_def.fields.Add(name, &field))
340 Error("field already exists: " + name);
341 return field;
342}
343
344void Parser::ParseField(StructDef &struct_def) {
345 std::string name = attribute_;
346 std::string dc = doc_comment_;
347 Expect(kTokenIdentifier);
348 Expect(':');
349 Type type;
350 ParseType(type);
351
352 if (struct_def.fixed && !IsScalar(type.base_type) && !IsStruct(type))
353 Error("structs_ may contain only scalar or struct fields");
354
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700355 FieldDef *typefield = nullptr;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800356 if (type.base_type == BASE_TYPE_UNION) {
357 // For union fields, add a second auto-generated field to hold the type,
358 // with _type appended as the name.
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700359 typefield = &AddField(struct_def, name + "_type",
360 type.enum_def->underlying_type);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800361 }
362
363 auto &field = AddField(struct_def, name, type);
364
365 if (token_ == '=') {
366 Next();
Wouter van Oortmerssen15dc1a82014-09-03 12:23:15 -0700367 if (!IsScalar(type.base_type))
368 Error("default values currently only supported for scalars");
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800369 ParseSingleValue(field.value);
370 }
371
372 field.doc_comment = dc;
373 ParseMetaData(field);
374 field.deprecated = field.attributes.Lookup("deprecated") != nullptr;
375 if (field.deprecated && struct_def.fixed)
376 Error("can't deprecate fields in a struct");
Wouter van Oortmerssen3e201a92014-07-15 17:50:22 -0700377 auto nested = field.attributes.Lookup("nested_flatbuffer");
378 if (nested) {
379 if (nested->type.base_type != BASE_TYPE_STRING)
380 Error("nested_flatbuffer attribute must be a string (the root type)");
381 if (field.value.type.base_type != BASE_TYPE_VECTOR ||
382 field.value.type.element != BASE_TYPE_UCHAR)
383 Error("nested_flatbuffer attribute may only apply to a vector of ubyte");
384 // This will cause an error if the root type of the nested flatbuffer
385 // wasn't defined elsewhere.
386 LookupCreateStruct(nested->constant);
387 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800388
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700389 if (typefield) {
390 // If this field is a union, and it has a manually assigned id,
391 // the automatically added type field should have an id as well (of N - 1).
392 auto attr = field.attributes.Lookup("id");
393 if (attr) {
394 auto id = atoi(attr->constant.c_str());
395 auto val = new Value();
396 val->type = attr->type;
397 val->constant = NumToString(id - 1);
398 typefield->attributes.Add("id", val);
399 }
400 }
401
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800402 Expect(';');
403}
404
405void Parser::ParseAnyValue(Value &val, FieldDef *field) {
406 switch (val.type.base_type) {
407 case BASE_TYPE_UNION: {
408 assert(field);
409 if (!field_stack_.size() ||
410 field_stack_.back().second->value.type.base_type != BASE_TYPE_UTYPE)
411 Error("missing type field before this union value: " + field->name);
412 auto enum_idx = atot<unsigned char>(
413 field_stack_.back().first.constant.c_str());
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700414 auto enum_val = val.type.enum_def->ReverseLookup(enum_idx);
415 if (!enum_val) Error("illegal type id for: " + field->name);
416 val.constant = NumToString(ParseTable(*enum_val->struct_def));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800417 break;
418 }
419 case BASE_TYPE_STRUCT:
420 val.constant = NumToString(ParseTable(*val.type.struct_def));
421 break;
422 case BASE_TYPE_STRING: {
423 auto s = attribute_;
424 Expect(kTokenStringConstant);
425 val.constant = NumToString(builder_.CreateString(s).o);
426 break;
427 }
428 case BASE_TYPE_VECTOR: {
429 Expect('[');
430 val.constant = NumToString(ParseVector(val.type.VectorType()));
431 break;
432 }
433 default:
434 ParseSingleValue(val);
435 break;
436 }
437}
438
439void Parser::SerializeStruct(const StructDef &struct_def, const Value &val) {
440 auto off = atot<uoffset_t>(val.constant.c_str());
441 assert(struct_stack_.size() - off == struct_def.bytesize);
442 builder_.Align(struct_def.minalign);
443 builder_.PushBytes(&struct_stack_[off], struct_def.bytesize);
444 struct_stack_.resize(struct_stack_.size() - struct_def.bytesize);
445 builder_.AddStructOffset(val.offset, builder_.GetSize());
446}
447
448uoffset_t Parser::ParseTable(const StructDef &struct_def) {
449 Expect('{');
450 size_t fieldn = 0;
Wouter van Oortmerssen0b47e692014-08-12 16:20:13 -0700451 if (!IsNext('}')) for (;;) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800452 std::string name = attribute_;
453 if (!IsNext(kTokenStringConstant)) Expect(kTokenIdentifier);
454 auto field = struct_def.fields.Lookup(name);
455 if (!field) Error("unknown field: " + name);
456 if (struct_def.fixed && (fieldn >= struct_def.fields.vec.size()
457 || struct_def.fields.vec[fieldn] != field)) {
458 Error("struct field appearing out of order: " + name);
459 }
460 Expect(':');
461 Value val = field->value;
462 ParseAnyValue(val, field);
463 field_stack_.push_back(std::make_pair(val, field));
464 fieldn++;
465 if (IsNext('}')) break;
466 Expect(',');
467 }
Wouter van Oortmerssen11f25382014-09-04 11:57:09 -0700468 for (auto it = field_stack_.rbegin();
469 it != field_stack_.rbegin() + fieldn; ++it) {
470 if (it->second->used)
471 Error("field set more than once: " + it->second->name);
472 it->second->used = true;
473 }
474 for (auto it = field_stack_.rbegin();
475 it != field_stack_.rbegin() + fieldn; ++it) {
476 it->second->used = false;
477 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800478 if (struct_def.fixed && fieldn != struct_def.fields.vec.size())
479 Error("incomplete struct initialization: " + struct_def.name);
480 auto start = struct_def.fixed
481 ? builder_.StartStruct(struct_def.minalign)
482 : builder_.StartTable();
483
484 for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
485 size;
486 size /= 2) {
487 // Go through elements in reverse, since we're building the data backwards.
488 for (auto it = field_stack_.rbegin();
489 it != field_stack_.rbegin() + fieldn; ++it) {
490 auto &value = it->first;
491 auto field = it->second;
492 if (!struct_def.sortbysize || size == SizeOf(value.type.base_type)) {
493 switch (value.type.base_type) {
rw74d5f372014-07-11 16:12:35 -0700494 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800495 case BASE_TYPE_ ## ENUM: \
496 builder_.Pad(field->padding); \
Wouter van Oortmerssenbe3c8742014-08-11 17:38:54 -0700497 if (struct_def.fixed) { \
498 builder_.PushElement(atot<CTYPE>(value.constant.c_str())); \
499 } else { \
500 builder_.AddElement(value.offset, \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800501 atot<CTYPE>( value.constant.c_str()), \
502 atot<CTYPE>(field->value.constant.c_str())); \
Wouter van Oortmerssenbe3c8742014-08-11 17:38:54 -0700503 } \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800504 break;
505 FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
506 #undef FLATBUFFERS_TD
rw74d5f372014-07-11 16:12:35 -0700507 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800508 case BASE_TYPE_ ## ENUM: \
509 builder_.Pad(field->padding); \
510 if (IsStruct(field->value.type)) { \
511 SerializeStruct(*field->value.type.struct_def, value); \
512 } else { \
513 builder_.AddOffset(value.offset, \
514 atot<CTYPE>(value.constant.c_str())); \
515 } \
516 break;
517 FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD);
518 #undef FLATBUFFERS_TD
519 }
520 }
521 }
522 }
523 for (size_t i = 0; i < fieldn; i++) field_stack_.pop_back();
524
525 if (struct_def.fixed) {
526 builder_.ClearOffsets();
527 builder_.EndStruct();
528 // Temporarily store this struct in a side buffer, since this data has to
529 // be stored in-line later in the parent object.
530 auto off = struct_stack_.size();
531 struct_stack_.insert(struct_stack_.end(),
532 builder_.GetBufferPointer(),
533 builder_.GetBufferPointer() + struct_def.bytesize);
534 builder_.PopBytes(struct_def.bytesize);
535 return static_cast<uoffset_t>(off);
536 } else {
537 return builder_.EndTable(
538 start,
539 static_cast<voffset_t>(struct_def.fields.vec.size()));
540 }
541}
542
543uoffset_t Parser::ParseVector(const Type &type) {
544 int count = 0;
545 if (token_ != ']') for (;;) {
546 Value val;
547 val.type = type;
548 ParseAnyValue(val, NULL);
549 field_stack_.push_back(std::make_pair(val, nullptr));
550 count++;
551 if (token_ == ']') break;
552 Expect(',');
553 }
554 Next();
555
Wouter van Oortmerssenbe3c8742014-08-11 17:38:54 -0700556 builder_.StartVector(count * InlineSize(type) / InlineAlignment(type),
557 InlineAlignment(type));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800558 for (int i = 0; i < count; i++) {
559 // start at the back, since we're building the data backwards.
560 auto &val = field_stack_.back().first;
561 switch (val.type.base_type) {
rw74d5f372014-07-11 16:12:35 -0700562 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE) \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800563 case BASE_TYPE_ ## ENUM: \
564 if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \
565 else builder_.PushElement(atot<CTYPE>(val.constant.c_str())); \
566 break;
567 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
568 #undef FLATBUFFERS_TD
569 }
570 field_stack_.pop_back();
571 }
572
573 builder_.ClearOffsets();
574 return builder_.EndVector(count);
575}
576
577void Parser::ParseMetaData(Definition &def) {
578 if (IsNext('(')) {
579 for (;;) {
580 auto name = attribute_;
581 Expect(kTokenIdentifier);
582 auto e = new Value();
583 def.attributes.Add(name, e);
584 if (IsNext(':')) {
585 ParseSingleValue(*e);
586 }
587 if (IsNext(')')) break;
588 Expect(',');
589 }
590 }
591}
592
593bool Parser::TryTypedValue(int dtoken,
594 bool check,
595 Value &e,
596 BaseType req) {
597 bool match = dtoken == token_;
598 if (match) {
599 e.constant = attribute_;
600 if (!check) {
601 if (e.type.base_type == BASE_TYPE_NONE) {
602 e.type.base_type = req;
603 } else {
604 Error(std::string("type mismatch: expecting: ") +
605 kTypeNames[e.type.base_type] +
606 ", found: " +
607 kTypeNames[req]);
608 }
609 }
610 Next();
611 }
612 return match;
613}
614
Wouter van Oortmerssen9c3de1e2014-07-25 15:04:35 -0700615int64_t Parser::ParseIntegerFromString(Type &type) {
616 int64_t result = 0;
617 // Parse one or more enum identifiers, separated by spaces.
618 const char *next = attribute_.c_str();
619 do {
620 const char *divider = strchr(next, ' ');
621 std::string word;
622 if (divider) {
623 word = std::string(next, divider);
624 next = divider + strspn(divider, " ");
625 } else {
626 word = next;
627 next += word.length();
628 }
629 if (type.enum_def) { // The field has an enum type
630 auto enum_val = type.enum_def->vals.Lookup(word);
631 if (!enum_val)
632 Error("unknown enum value: " + word +
633 ", for enum: " + type.enum_def->name);
634 result |= enum_val->value;
635 } else { // No enum type, probably integral field.
636 if (!IsInteger(type.base_type))
637 Error("not a valid value for this field: " + word);
638 // TODO: could check if its a valid number constant here.
639 const char *dot = strchr(word.c_str(), '.');
640 if (!dot) Error("enum values need to be qualified by an enum type");
641 std::string enum_def_str(word.c_str(), dot);
642 std::string enum_val_str(dot + 1, word.c_str() + word.length());
643 auto enum_def = enums_.Lookup(enum_def_str);
644 if (!enum_def) Error("unknown enum: " + enum_def_str);
645 auto enum_val = enum_def->vals.Lookup(enum_val_str);
646 if (!enum_val) Error("unknown enum value: " + enum_val_str);
647 result |= enum_val->value;
648 }
649 } while(*next);
650 return result;
651}
652
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800653void Parser::ParseSingleValue(Value &e) {
Wouter van Oortmerssen9c3de1e2014-07-25 15:04:35 -0700654 // First check if this could be a string/identifier enum value:
655 if (e.type.base_type != BASE_TYPE_STRING &&
656 e.type.base_type != BASE_TYPE_NONE &&
657 (token_ == kTokenIdentifier || token_ == kTokenStringConstant)) {
658 e.constant = NumToString(ParseIntegerFromString(e.type));
659 Next();
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700660 } else if (TryTypedValue(kTokenIntegerConstant,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800661 IsScalar(e.type.base_type),
662 e,
663 BASE_TYPE_INT) ||
664 TryTypedValue(kTokenFloatConstant,
665 IsFloat(e.type.base_type),
666 e,
667 BASE_TYPE_FLOAT) ||
668 TryTypedValue(kTokenStringConstant,
669 e.type.base_type == BASE_TYPE_STRING,
670 e,
671 BASE_TYPE_STRING)) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800672 } else {
673 Error("cannot parse value starting with: " + TokenToString(token_));
674 }
675}
676
677StructDef *Parser::LookupCreateStruct(const std::string &name) {
678 auto struct_def = structs_.Lookup(name);
679 if (!struct_def) {
680 // Rather than failing, we create a "pre declared" StructDef, due to
681 // circular references, and check for errors at the end of parsing.
682 struct_def = new StructDef();
683 structs_.Add(name, struct_def);
684 struct_def->name = name;
685 struct_def->predecl = true;
Wouter van Oortmerssenc2ba7fd2014-08-19 16:37:46 -0700686 struct_def->defined_namespace = namespaces_.back();
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800687 }
688 return struct_def;
689}
690
691void Parser::ParseEnum(bool is_union) {
692 std::string dc = doc_comment_;
693 Next();
694 std::string name = attribute_;
695 Expect(kTokenIdentifier);
696 auto &enum_def = *new EnumDef();
697 enum_def.name = name;
698 enum_def.doc_comment = dc;
699 enum_def.is_union = is_union;
700 if (enums_.Add(name, &enum_def)) Error("enum already exists: " + name);
701 if (is_union) {
702 enum_def.underlying_type.base_type = BASE_TYPE_UTYPE;
703 enum_def.underlying_type.enum_def = &enum_def;
Wouter van Oortmerssena5f50012014-07-02 12:01:21 -0700704 } else {
705 // Give specialized error message, since this type spec used to
706 // be optional in the first FlatBuffers release.
707 if (!IsNext(':')) Error("must specify the underlying integer type for this"
708 " enum (e.g. \': short\', which was the default).");
709 // Specify the integer type underlying this enum.
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800710 ParseType(enum_def.underlying_type);
711 if (!IsInteger(enum_def.underlying_type.base_type))
712 Error("underlying enum type must be integral");
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700713 // Make this type refer back to the enum it was derived from.
714 enum_def.underlying_type.enum_def = &enum_def;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800715 }
716 ParseMetaData(enum_def);
717 Expect('{');
718 if (is_union) enum_def.vals.Add("NONE", new EnumVal("NONE", 0));
719 do {
720 std::string name = attribute_;
721 std::string dc = doc_comment_;
722 Expect(kTokenIdentifier);
723 auto prevsize = enum_def.vals.vec.size();
Wouter van Oortmerssen127d3502014-07-17 15:12:37 -0700724 auto value = enum_def.vals.vec.size()
725 ? enum_def.vals.vec.back()->value + 1
726 : 0;
727 auto &ev = *new EnumVal(name, value);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800728 if (enum_def.vals.Add(name, &ev))
729 Error("enum value already exists: " + name);
730 ev.doc_comment = dc;
731 if (is_union) {
732 ev.struct_def = LookupCreateStruct(name);
733 }
734 if (IsNext('=')) {
735 ev.value = atoi(attribute_.c_str());
736 Expect(kTokenIntegerConstant);
737 if (prevsize && enum_def.vals.vec[prevsize - 1]->value >= ev.value)
738 Error("enum values must be specified in ascending order");
739 }
Wouter van Oortmerssenc553b6b2014-08-21 15:17:45 -0700740 } while (IsNext(',') && token_ != '}');
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800741 Expect('}');
Wouter van Oortmerssen127d3502014-07-17 15:12:37 -0700742 if (enum_def.attributes.Lookup("bit_flags")) {
743 for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
744 ++it) {
745 if (static_cast<size_t>((*it)->value) >=
746 SizeOf(enum_def.underlying_type.base_type) * 8)
747 Error("bit flag out of range of underlying integral type");
Wouter van Oortmerssen9c3de1e2014-07-25 15:04:35 -0700748 (*it)->value = 1LL << (*it)->value;
Wouter van Oortmerssen127d3502014-07-17 15:12:37 -0700749 }
750 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800751}
752
753void Parser::ParseDecl() {
754 std::string dc = doc_comment_;
755 bool fixed = IsNext(kTokenStruct);
756 if (!fixed) Expect(kTokenTable);
757 std::string name = attribute_;
758 Expect(kTokenIdentifier);
759 auto &struct_def = *LookupCreateStruct(name);
760 if (!struct_def.predecl) Error("datatype already exists: " + name);
761 struct_def.predecl = false;
762 struct_def.name = name;
763 struct_def.doc_comment = dc;
764 struct_def.fixed = fixed;
765 // Move this struct to the back of the vector just in case it was predeclared,
766 // to preserve declartion order.
767 remove(structs_.vec.begin(), structs_.vec.end(), &struct_def);
768 structs_.vec.back() = &struct_def;
769 ParseMetaData(struct_def);
770 struct_def.sortbysize =
771 struct_def.attributes.Lookup("original_order") == nullptr && !fixed;
772 Expect('{');
773 while (token_ != '}') ParseField(struct_def);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800774 auto force_align = struct_def.attributes.Lookup("force_align");
775 if (fixed && force_align) {
776 auto align = static_cast<size_t>(atoi(force_align->constant.c_str()));
777 if (force_align->type.base_type != BASE_TYPE_INT ||
778 align < struct_def.minalign ||
779 align > 256 ||
780 align & (align - 1))
781 Error("force_align must be a power of two integer ranging from the"
782 "struct\'s natural alignment to 256");
783 struct_def.minalign = align;
784 }
Wouter van Oortmerssen65cfa182014-06-23 11:34:19 -0700785 struct_def.PadLastField(struct_def.minalign);
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700786 // Check if this is a table that has manual id assignments
787 auto &fields = struct_def.fields.vec;
788 if (!struct_def.fixed && fields.size()) {
Wouter van Oortmerssen7fcbe722014-07-08 16:35:14 -0700789 size_t num_id_fields = 0;
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700790 for (auto it = fields.begin(); it != fields.end(); ++it) {
791 if ((*it)->attributes.Lookup("id")) num_id_fields++;
792 }
793 // If any fields have ids..
794 if (num_id_fields) {
795 // Then all fields must have them.
796 if (num_id_fields != fields.size())
797 Error("either all fields or no fields must have an 'id' attribute");
798 // Simply sort by id, then the fields are the same as if no ids had
799 // been specified.
800 std::sort(fields.begin(), fields.end(),
801 [](const FieldDef *a, const FieldDef *b) -> bool {
802 auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str());
803 auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str());
804 return a_id < b_id;
805 });
806 // Verify we have a contiguous set, and reassign vtable offsets.
807 for (int i = 0; i < static_cast<int>(fields.size()); i++) {
808 if (i != atoi(fields[i]->attributes.Lookup("id")->constant.c_str()))
809 Error("field id\'s must be consecutive from 0, id " +
810 NumToString(i) + " missing or set twice");
811 fields[i]->value.offset = FieldIndexToOffset(static_cast<voffset_t>(i));
812 }
813 }
814 }
Wouter van Oortmerssen541b0672014-08-21 15:02:15 -0700815 // Check that no identifiers clash with auto generated fields.
816 // This is not an ideal situation, but should occur very infrequently,
817 // and allows us to keep using very readable names for type & length fields
818 // without inducing compile errors.
819 auto CheckClash = [&fields, &struct_def](const char *suffix,
820 BaseType basetype) {
821 auto len = strlen(suffix);
822 for (auto it = fields.begin(); it != fields.end(); ++it) {
823 auto &name = (*it)->name;
824 if (name.length() > len &&
825 name.compare(name.length() - len, len, suffix) == 0 &&
826 (*it)->value.type.base_type != BASE_TYPE_UTYPE) {
827 auto field = struct_def.fields.Lookup(
828 name.substr(0, name.length() - len));
829 if (field && field->value.type.base_type == basetype)
830 Error("Field " + name +
831 " would clash with generated functions for field " +
832 field->name);
833 }
834 }
835 };
836 CheckClash("_type", BASE_TYPE_UNION);
837 CheckClash("Type", BASE_TYPE_UNION);
838 CheckClash("_length", BASE_TYPE_VECTOR);
839 CheckClash("Length", BASE_TYPE_VECTOR);
Wouter van Oortmerssen65cfa182014-06-23 11:34:19 -0700840 Expect('}');
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800841}
842
843bool Parser::SetRootType(const char *name) {
844 root_struct_def = structs_.Lookup(name);
845 return root_struct_def != nullptr;
846}
847
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700848void Parser::MarkGenerated() {
849 // Since the Parser object retains definitions across files, we must
850 // ensure we only output code for definitions once, in the file they are first
851 // declared. This function marks all existing definitions as having already
852 // been generated.
853 for (auto it = enums_.vec.begin();
854 it != enums_.vec.end(); ++it) {
855 (*it)->generated = true;
856 }
857 for (auto it = structs_.vec.begin();
858 it != structs_.vec.end(); ++it) {
859 (*it)->generated = true;
860 }
861}
862
Wouter van Oortmerssene57b86b2014-09-11 17:13:21 -0700863bool Parser::Parse(const char *source, const char **include_paths) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800864 source_ = cursor_ = source;
865 line_ = 1;
866 error_.clear();
867 builder_.Clear();
868 try {
869 Next();
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700870 // Includes must come first:
871 while (IsNext(kTokenInclude)) {
872 auto name = attribute_;
873 Expect(kTokenStringConstant);
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700874 if (included_files_.find(name) == included_files_.end()) {
875 // We found an include file that we have not parsed yet.
876 // Load it and parse it.
877 std::string contents;
Wouter van Oortmerssene57b86b2014-09-11 17:13:21 -0700878 if (!include_paths) {
879 const char *current_directory[] = { "", nullptr };
880 include_paths = current_directory;
881 }
882 for (auto paths = include_paths; paths && *paths; paths++) {
883 auto filepath = flatbuffers::ConCatPathFileName(*paths, name);
884 if(LoadFile(filepath.c_str(), true, &contents)) break;
885 }
886 if (contents.empty())
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700887 Error("unable to load include file: " + name);
Wouter van Oortmerssene57b86b2014-09-11 17:13:21 -0700888 included_files_[name] = true;
889 if (!Parse(contents.c_str(), include_paths)) {
890 // Any errors, we're done.
891 return false;
892 }
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700893 // We do not want to output code for any included files:
894 MarkGenerated();
895 // This is the easiest way to continue this file after an include:
896 // instead of saving and restoring all the state, we simply start the
897 // file anew. This will cause it to encounter the same include statement
898 // again, but this time it will skip it, because it was entered into
899 // included_files_.
Wouter van Oortmerssene57b86b2014-09-11 17:13:21 -0700900 // This is recursive, but only go as deep as the number of include
901 // statements.
902 return Parse(source, include_paths);
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700903 }
904 Expect(';');
905 }
906 // Now parse all other kinds of declarations:
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800907 while (token_ != kTokenEof) {
908 if (token_ == kTokenNameSpace) {
909 Next();
Wouter van Oortmerssenc2ba7fd2014-08-19 16:37:46 -0700910 auto ns = new Namespace();
911 namespaces_.push_back(ns);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800912 for (;;) {
Wouter van Oortmerssenc2ba7fd2014-08-19 16:37:46 -0700913 ns->components.push_back(attribute_);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800914 Expect(kTokenIdentifier);
915 if (!IsNext('.')) break;
916 }
917 Expect(';');
918 } else if (token_ == '{') {
919 if (!root_struct_def) Error("no root type set to parse json with");
920 if (builder_.GetSize()) {
921 Error("cannot have more than one json object in a file");
922 }
Wouter van Oortmerssen09a29992014-09-04 16:31:44 -0700923 builder_.Finish(Offset<Table>(ParseTable(*root_struct_def)),
924 file_identifier_.length() ? file_identifier_.c_str() : nullptr);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800925 } else if (token_ == kTokenEnum) {
926 ParseEnum(false);
927 } else if (token_ == kTokenUnion) {
928 ParseEnum(true);
929 } else if (token_ == kTokenRootType) {
930 Next();
931 auto root_type = attribute_;
932 Expect(kTokenIdentifier);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800933 if (!SetRootType(root_type.c_str()))
934 Error("unknown root type: " + root_type);
935 if (root_struct_def->fixed)
936 Error("root type must be a table");
Wouter van Oortmerssen5da7bda2014-07-31 15:11:03 -0700937 Expect(';');
938 } else if (token_ == kTokenFileIdentifier) {
939 Next();
940 file_identifier_ = attribute_;
941 Expect(kTokenStringConstant);
942 if (file_identifier_.length() !=
943 FlatBufferBuilder::kFileIdentifierLength)
944 Error("file_identifier must be exactly " +
945 NumToString(FlatBufferBuilder::kFileIdentifierLength) +
946 " characters");
947 Expect(';');
948 } else if (token_ == kTokenFileExtension) {
949 Next();
950 file_extension_ = attribute_;
951 Expect(kTokenStringConstant);
952 Expect(';');
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700953 } else if(token_ == kTokenInclude) {
954 Error("includes must come before declarations");
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800955 } else {
956 ParseDecl();
957 }
958 }
959 for (auto it = structs_.vec.begin(); it != structs_.vec.end(); ++it) {
960 if ((*it)->predecl)
961 Error("type referenced but not defined: " + (*it)->name);
962 }
963 for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
964 auto &enum_def = **it;
965 if (enum_def.is_union) {
966 for (auto it = enum_def.vals.vec.begin();
967 it != enum_def.vals.vec.end();
968 ++it) {
969 auto &val = **it;
970 if (val.struct_def && val.struct_def->fixed)
971 Error("only tables can be union elements: " + val.name);
972 }
973 }
974 }
975 } catch (const std::string &msg) {
976 error_ = "line " + NumToString(line_) + ": " + msg;
977 return false;
978 }
979 assert(!struct_stack_.size());
980 return true;
981}
982
983} // namespace flatbuffers