blob: e1f180cc0ffd880552efdae2f0f5f8d3dacd2a7b [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") \
88 TD(Include, 269, "include")
Wouter van Oortmerssen8f80fec2014-07-29 10:29:38 -070089#ifdef __GNUC__
90__extension__ // Stop GCC complaining about trailing comma with -Wpendantic.
91#endif
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080092enum {
Wouter van Oortmerssen75349ae2014-07-09 11:44:26 -070093 #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) kToken ## NAME = VALUE,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080094 FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
95 #undef FLATBUFFERS_TOKEN
Wouter van Oortmerssen557c88c2014-09-16 17:37:17 -070096 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) \
97 kToken ## ENUM,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -080098 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
99 #undef FLATBUFFERS_TD
100};
101
102static std::string TokenToString(int t) {
103 static const char *tokens[] = {
104 #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) STRING,
105 FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
106 #undef FLATBUFFERS_TOKEN
Wouter van Oortmerssen557c88c2014-09-16 17:37:17 -0700107 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) IDLTYPE,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800108 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
109 #undef FLATBUFFERS_TD
110 };
111 if (t < 256) { // A single ascii char token.
112 std::string s;
Wouter van Oortmerssen8e409022014-09-02 17:32:12 -0700113 s.append(1, static_cast<char>(t));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800114 return s;
115 } else { // Other tokens.
116 return tokens[t - 256];
117 }
118}
119
Wouter van Oortmerssenebac1e12014-08-20 13:22:16 -0700120// Parses exactly nibbles worth of hex digits into a number, or error.
121int64_t Parser::ParseHexNum(int nibbles) {
122 for (int i = 0; i < nibbles; i++)
123 if (!isxdigit(cursor_[i]))
124 Error("escape code must be followed by " + NumToString(nibbles) +
125 " hex digits");
126 auto val = StringToInt(cursor_, 16);
127 cursor_ += nibbles;
128 return val;
129}
130
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800131void Parser::Next() {
132 doc_comment_.clear();
133 bool seen_newline = false;
134 for (;;) {
135 char c = *cursor_++;
136 token_ = c;
137 switch (c) {
138 case '\0': cursor_--; token_ = kTokenEof; return;
139 case ' ': case '\r': case '\t': break;
140 case '\n': line_++; seen_newline = true; break;
141 case '{': case '}': case '(': case ')': case '[': case ']': return;
142 case ',': case ':': case ';': case '=': return;
143 case '.':
144 if(!isdigit(*cursor_)) return;
145 Error("floating point constant can\'t start with \".\"");
146 break;
147 case '\"':
148 attribute_ = "";
149 while (*cursor_ != '\"') {
150 if (*cursor_ < ' ' && *cursor_ >= 0)
151 Error("illegal character in string constant");
152 if (*cursor_ == '\\') {
153 cursor_++;
154 switch (*cursor_) {
155 case 'n': attribute_ += '\n'; cursor_++; break;
156 case 't': attribute_ += '\t'; cursor_++; break;
157 case 'r': attribute_ += '\r'; cursor_++; break;
Wouter van Oortmerssenebac1e12014-08-20 13:22:16 -0700158 case 'b': attribute_ += '\b'; cursor_++; break;
159 case 'f': attribute_ += '\f'; cursor_++; break;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800160 case '\"': attribute_ += '\"'; cursor_++; break;
161 case '\\': attribute_ += '\\'; cursor_++; break;
Wouter van Oortmerssenebac1e12014-08-20 13:22:16 -0700162 case '/': attribute_ += '/'; cursor_++; break;
163 case 'x': { // Not in the JSON standard
164 cursor_++;
165 attribute_ += static_cast<char>(ParseHexNum(2));
166 break;
167 }
168 case 'u': {
169 cursor_++;
170 ToUTF8(static_cast<int>(ParseHexNum(4)), &attribute_);
171 break;
172 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800173 default: Error("unknown escape code in string constant"); break;
174 }
175 } else { // printable chars + UTF-8 bytes
176 attribute_ += *cursor_++;
177 }
178 }
179 cursor_++;
180 token_ = kTokenStringConstant;
181 return;
182 case '/':
183 if (*cursor_ == '/') {
184 const char *start = ++cursor_;
185 while (*cursor_ && *cursor_ != '\n') cursor_++;
186 if (*start == '/') { // documentation comment
187 if (!seen_newline)
188 Error("a documentation comment should be on a line on its own");
189 // todo: do we want to support multiline comments instead?
190 doc_comment_ += std::string(start + 1, cursor_);
191 }
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 Oortmerssenbe894f02014-08-19 14:20:05 -0700226 if (attribute_ == "include") { token_ = kTokenInclude; return; }
Wouter van Oortmerssen5da7bda2014-07-31 15:11:03 -0700227 if (attribute_ == "file_identifier") {
228 token_ = kTokenFileIdentifier;
229 return;
230 }
231 if (attribute_ == "file_extension") {
232 token_ = kTokenFileExtension;
233 return;
234 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800235 // If not, it is a user-defined identifier:
236 token_ = kTokenIdentifier;
237 return;
238 } else if (isdigit(static_cast<unsigned char>(c)) || c == '-') {
239 const char *start = cursor_ - 1;
240 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
241 if (*cursor_ == '.') {
242 cursor_++;
243 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
Wouter van Oortmerssen93df5692014-07-10 13:40:55 -0700244 // See if this float has a scientific notation suffix. Both JSON
245 // and C++ (through strtod() we use) have the same format:
246 if (*cursor_ == 'e' || *cursor_ == 'E') {
247 cursor_++;
248 if (*cursor_ == '+' || *cursor_ == '-') cursor_++;
249 while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
250 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800251 token_ = kTokenFloatConstant;
252 } else {
253 token_ = kTokenIntegerConstant;
254 }
255 attribute_.clear();
256 attribute_.append(start, cursor_);
257 return;
258 }
259 std::string ch;
260 ch = c;
261 if (c < ' ' || c > '~') ch = "code: " + NumToString(c);
262 Error("illegal character: " + ch);
263 break;
264 }
265 }
266}
267
268// Check if a given token is next, if so, consume it as well.
269bool Parser::IsNext(int t) {
270 bool isnext = t == token_;
271 if (isnext) Next();
272 return isnext;
273}
274
275// Expect a given token to be next, consume it, or error if not present.
276void Parser::Expect(int t) {
277 if (t != token_) {
278 Error("expecting: " + TokenToString(t) + " instead got: " +
279 TokenToString(token_));
280 }
281 Next();
282}
283
284// Parse any IDL type.
285void Parser::ParseType(Type &type) {
286 if (token_ >= kTokenBOOL && token_ <= kTokenSTRING) {
287 type.base_type = static_cast<BaseType>(token_ - kTokenNONE);
288 } else {
289 if (token_ == kTokenIdentifier) {
290 auto enum_def = enums_.Lookup(attribute_);
291 if (enum_def) {
292 type = enum_def->underlying_type;
293 if (enum_def->is_union) type.base_type = BASE_TYPE_UNION;
294 } else {
295 type.base_type = BASE_TYPE_STRUCT;
296 type.struct_def = LookupCreateStruct(attribute_);
297 }
298 } else if (token_ == '[') {
299 Next();
300 Type subtype;
301 ParseType(subtype);
302 if (subtype.base_type == BASE_TYPE_VECTOR) {
303 // We could support this, but it will complicate things, and it's
304 // easier to work around with a struct around the inner vector.
305 Error("nested vector types not supported (wrap in table first).");
306 }
307 if (subtype.base_type == BASE_TYPE_UNION) {
308 // We could support this if we stored a struct of 2 elements per
309 // union element.
310 Error("vector of union types not supported (wrap in table first).");
311 }
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700312 type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800313 type.element = subtype.base_type;
314 Expect(']');
315 return;
316 } else {
317 Error("illegal type syntax");
318 }
319 }
320 Next();
321}
322
323FieldDef &Parser::AddField(StructDef &struct_def,
324 const std::string &name,
325 const Type &type) {
326 auto &field = *new FieldDef();
327 field.value.offset =
328 FieldIndexToOffset(static_cast<voffset_t>(struct_def.fields.vec.size()));
329 field.name = name;
330 field.value.type = type;
331 if (struct_def.fixed) { // statically compute the field offset
332 auto size = InlineSize(type);
333 auto alignment = InlineAlignment(type);
334 // structs_ need to have a predictable format, so we need to align to
335 // the largest scalar
336 struct_def.minalign = std::max(struct_def.minalign, alignment);
337 struct_def.PadLastField(alignment);
Wouter van Oortmerssen12563072014-06-30 15:56:31 -0700338 field.value.offset = static_cast<voffset_t>(struct_def.bytesize);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800339 struct_def.bytesize += size;
340 }
341 if (struct_def.fields.Add(name, &field))
342 Error("field already exists: " + name);
343 return field;
344}
345
346void Parser::ParseField(StructDef &struct_def) {
347 std::string name = attribute_;
348 std::string dc = doc_comment_;
349 Expect(kTokenIdentifier);
350 Expect(':');
351 Type type;
352 ParseType(type);
353
354 if (struct_def.fixed && !IsScalar(type.base_type) && !IsStruct(type))
355 Error("structs_ may contain only scalar or struct fields");
356
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700357 FieldDef *typefield = nullptr;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800358 if (type.base_type == BASE_TYPE_UNION) {
359 // For union fields, add a second auto-generated field to hold the type,
360 // with _type appended as the name.
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700361 typefield = &AddField(struct_def, name + "_type",
362 type.enum_def->underlying_type);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800363 }
364
365 auto &field = AddField(struct_def, name, type);
366
367 if (token_ == '=') {
368 Next();
Wouter van Oortmerssen15dc1a82014-09-03 12:23:15 -0700369 if (!IsScalar(type.base_type))
370 Error("default values currently only supported for scalars");
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800371 ParseSingleValue(field.value);
372 }
373
374 field.doc_comment = dc;
375 ParseMetaData(field);
376 field.deprecated = field.attributes.Lookup("deprecated") != nullptr;
377 if (field.deprecated && struct_def.fixed)
378 Error("can't deprecate fields in a struct");
Wouter van Oortmerssen517c9642014-09-19 16:51:36 -0700379 field.required = field.attributes.Lookup("required") != nullptr;
380 if (field.required && (struct_def.fixed ||
381 IsScalar(field.value.type.base_type)))
382 Error("only non-scalar fields in tables may be 'required'");
Wouter van Oortmerssen3e201a92014-07-15 17:50:22 -0700383 auto nested = field.attributes.Lookup("nested_flatbuffer");
384 if (nested) {
385 if (nested->type.base_type != BASE_TYPE_STRING)
386 Error("nested_flatbuffer attribute must be a string (the root type)");
387 if (field.value.type.base_type != BASE_TYPE_VECTOR ||
388 field.value.type.element != BASE_TYPE_UCHAR)
389 Error("nested_flatbuffer attribute may only apply to a vector of ubyte");
390 // This will cause an error if the root type of the nested flatbuffer
391 // wasn't defined elsewhere.
392 LookupCreateStruct(nested->constant);
393 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800394
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700395 if (typefield) {
396 // If this field is a union, and it has a manually assigned id,
397 // the automatically added type field should have an id as well (of N - 1).
398 auto attr = field.attributes.Lookup("id");
399 if (attr) {
400 auto id = atoi(attr->constant.c_str());
401 auto val = new Value();
402 val->type = attr->type;
403 val->constant = NumToString(id - 1);
404 typefield->attributes.Add("id", val);
405 }
406 }
407
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800408 Expect(';');
409}
410
411void Parser::ParseAnyValue(Value &val, FieldDef *field) {
412 switch (val.type.base_type) {
413 case BASE_TYPE_UNION: {
414 assert(field);
415 if (!field_stack_.size() ||
416 field_stack_.back().second->value.type.base_type != BASE_TYPE_UTYPE)
417 Error("missing type field before this union value: " + field->name);
418 auto enum_idx = atot<unsigned char>(
419 field_stack_.back().first.constant.c_str());
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700420 auto enum_val = val.type.enum_def->ReverseLookup(enum_idx);
421 if (!enum_val) Error("illegal type id for: " + field->name);
422 val.constant = NumToString(ParseTable(*enum_val->struct_def));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800423 break;
424 }
425 case BASE_TYPE_STRUCT:
426 val.constant = NumToString(ParseTable(*val.type.struct_def));
427 break;
428 case BASE_TYPE_STRING: {
429 auto s = attribute_;
430 Expect(kTokenStringConstant);
431 val.constant = NumToString(builder_.CreateString(s).o);
432 break;
433 }
434 case BASE_TYPE_VECTOR: {
435 Expect('[');
436 val.constant = NumToString(ParseVector(val.type.VectorType()));
437 break;
438 }
439 default:
440 ParseSingleValue(val);
441 break;
442 }
443}
444
445void Parser::SerializeStruct(const StructDef &struct_def, const Value &val) {
446 auto off = atot<uoffset_t>(val.constant.c_str());
447 assert(struct_stack_.size() - off == struct_def.bytesize);
448 builder_.Align(struct_def.minalign);
449 builder_.PushBytes(&struct_stack_[off], struct_def.bytesize);
450 struct_stack_.resize(struct_stack_.size() - struct_def.bytesize);
451 builder_.AddStructOffset(val.offset, builder_.GetSize());
452}
453
454uoffset_t Parser::ParseTable(const StructDef &struct_def) {
455 Expect('{');
456 size_t fieldn = 0;
Wouter van Oortmerssen0b47e692014-08-12 16:20:13 -0700457 if (!IsNext('}')) for (;;) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800458 std::string name = attribute_;
459 if (!IsNext(kTokenStringConstant)) Expect(kTokenIdentifier);
460 auto field = struct_def.fields.Lookup(name);
461 if (!field) Error("unknown field: " + name);
462 if (struct_def.fixed && (fieldn >= struct_def.fields.vec.size()
463 || struct_def.fields.vec[fieldn] != field)) {
464 Error("struct field appearing out of order: " + name);
465 }
466 Expect(':');
467 Value val = field->value;
468 ParseAnyValue(val, field);
469 field_stack_.push_back(std::make_pair(val, field));
470 fieldn++;
471 if (IsNext('}')) break;
472 Expect(',');
473 }
Wouter van Oortmerssen11f25382014-09-04 11:57:09 -0700474 for (auto it = field_stack_.rbegin();
475 it != field_stack_.rbegin() + fieldn; ++it) {
476 if (it->second->used)
477 Error("field set more than once: " + it->second->name);
478 it->second->used = true;
479 }
480 for (auto it = field_stack_.rbegin();
481 it != field_stack_.rbegin() + fieldn; ++it) {
482 it->second->used = false;
483 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800484 if (struct_def.fixed && fieldn != struct_def.fields.vec.size())
485 Error("incomplete struct initialization: " + struct_def.name);
486 auto start = struct_def.fixed
487 ? builder_.StartStruct(struct_def.minalign)
488 : builder_.StartTable();
489
490 for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
491 size;
492 size /= 2) {
493 // Go through elements in reverse, since we're building the data backwards.
494 for (auto it = field_stack_.rbegin();
495 it != field_stack_.rbegin() + fieldn; ++it) {
496 auto &value = it->first;
497 auto field = it->second;
498 if (!struct_def.sortbysize || size == SizeOf(value.type.base_type)) {
499 switch (value.type.base_type) {
Wouter van Oortmerssen557c88c2014-09-16 17:37:17 -0700500 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800501 case BASE_TYPE_ ## ENUM: \
502 builder_.Pad(field->padding); \
Wouter van Oortmerssenbe3c8742014-08-11 17:38:54 -0700503 if (struct_def.fixed) { \
504 builder_.PushElement(atot<CTYPE>(value.constant.c_str())); \
505 } else { \
506 builder_.AddElement(value.offset, \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800507 atot<CTYPE>( value.constant.c_str()), \
508 atot<CTYPE>(field->value.constant.c_str())); \
Wouter van Oortmerssenbe3c8742014-08-11 17:38:54 -0700509 } \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800510 break;
511 FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
512 #undef FLATBUFFERS_TD
Wouter van Oortmerssen557c88c2014-09-16 17:37:17 -0700513 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800514 case BASE_TYPE_ ## ENUM: \
515 builder_.Pad(field->padding); \
516 if (IsStruct(field->value.type)) { \
517 SerializeStruct(*field->value.type.struct_def, value); \
518 } else { \
519 builder_.AddOffset(value.offset, \
520 atot<CTYPE>(value.constant.c_str())); \
521 } \
522 break;
523 FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD);
524 #undef FLATBUFFERS_TD
525 }
526 }
527 }
528 }
529 for (size_t i = 0; i < fieldn; i++) field_stack_.pop_back();
530
531 if (struct_def.fixed) {
532 builder_.ClearOffsets();
533 builder_.EndStruct();
534 // Temporarily store this struct in a side buffer, since this data has to
535 // be stored in-line later in the parent object.
536 auto off = struct_stack_.size();
537 struct_stack_.insert(struct_stack_.end(),
538 builder_.GetBufferPointer(),
539 builder_.GetBufferPointer() + struct_def.bytesize);
540 builder_.PopBytes(struct_def.bytesize);
541 return static_cast<uoffset_t>(off);
542 } else {
543 return builder_.EndTable(
544 start,
545 static_cast<voffset_t>(struct_def.fields.vec.size()));
546 }
547}
548
549uoffset_t Parser::ParseVector(const Type &type) {
550 int count = 0;
551 if (token_ != ']') for (;;) {
552 Value val;
553 val.type = type;
554 ParseAnyValue(val, NULL);
555 field_stack_.push_back(std::make_pair(val, nullptr));
556 count++;
557 if (token_ == ']') break;
558 Expect(',');
559 }
560 Next();
561
Wouter van Oortmerssenbe3c8742014-08-11 17:38:54 -0700562 builder_.StartVector(count * InlineSize(type) / InlineAlignment(type),
563 InlineAlignment(type));
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800564 for (int i = 0; i < count; i++) {
565 // start at the back, since we're building the data backwards.
566 auto &val = field_stack_.back().first;
567 switch (val.type.base_type) {
Wouter van Oortmerssen557c88c2014-09-16 17:37:17 -0700568 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) \
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800569 case BASE_TYPE_ ## ENUM: \
570 if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \
571 else builder_.PushElement(atot<CTYPE>(val.constant.c_str())); \
572 break;
573 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
574 #undef FLATBUFFERS_TD
575 }
576 field_stack_.pop_back();
577 }
578
579 builder_.ClearOffsets();
580 return builder_.EndVector(count);
581}
582
583void Parser::ParseMetaData(Definition &def) {
584 if (IsNext('(')) {
585 for (;;) {
586 auto name = attribute_;
587 Expect(kTokenIdentifier);
588 auto e = new Value();
589 def.attributes.Add(name, e);
590 if (IsNext(':')) {
591 ParseSingleValue(*e);
592 }
593 if (IsNext(')')) break;
594 Expect(',');
595 }
596 }
597}
598
599bool Parser::TryTypedValue(int dtoken,
600 bool check,
601 Value &e,
602 BaseType req) {
603 bool match = dtoken == token_;
604 if (match) {
605 e.constant = attribute_;
606 if (!check) {
607 if (e.type.base_type == BASE_TYPE_NONE) {
608 e.type.base_type = req;
609 } else {
610 Error(std::string("type mismatch: expecting: ") +
611 kTypeNames[e.type.base_type] +
612 ", found: " +
613 kTypeNames[req]);
614 }
615 }
616 Next();
617 }
618 return match;
619}
620
Wouter van Oortmerssen9c3de1e2014-07-25 15:04:35 -0700621int64_t Parser::ParseIntegerFromString(Type &type) {
622 int64_t result = 0;
623 // Parse one or more enum identifiers, separated by spaces.
624 const char *next = attribute_.c_str();
625 do {
626 const char *divider = strchr(next, ' ');
627 std::string word;
628 if (divider) {
629 word = std::string(next, divider);
630 next = divider + strspn(divider, " ");
631 } else {
632 word = next;
633 next += word.length();
634 }
635 if (type.enum_def) { // The field has an enum type
636 auto enum_val = type.enum_def->vals.Lookup(word);
637 if (!enum_val)
638 Error("unknown enum value: " + word +
639 ", for enum: " + type.enum_def->name);
640 result |= enum_val->value;
641 } else { // No enum type, probably integral field.
642 if (!IsInteger(type.base_type))
643 Error("not a valid value for this field: " + word);
644 // TODO: could check if its a valid number constant here.
645 const char *dot = strchr(word.c_str(), '.');
646 if (!dot) Error("enum values need to be qualified by an enum type");
647 std::string enum_def_str(word.c_str(), dot);
648 std::string enum_val_str(dot + 1, word.c_str() + word.length());
649 auto enum_def = enums_.Lookup(enum_def_str);
650 if (!enum_def) Error("unknown enum: " + enum_def_str);
651 auto enum_val = enum_def->vals.Lookup(enum_val_str);
652 if (!enum_val) Error("unknown enum value: " + enum_val_str);
653 result |= enum_val->value;
654 }
655 } while(*next);
656 return result;
657}
658
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800659void Parser::ParseSingleValue(Value &e) {
Wouter van Oortmerssen9c3de1e2014-07-25 15:04:35 -0700660 // First check if this could be a string/identifier enum value:
661 if (e.type.base_type != BASE_TYPE_STRING &&
662 e.type.base_type != BASE_TYPE_NONE &&
663 (token_ == kTokenIdentifier || token_ == kTokenStringConstant)) {
664 e.constant = NumToString(ParseIntegerFromString(e.type));
665 Next();
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700666 } else if (TryTypedValue(kTokenIntegerConstant,
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800667 IsScalar(e.type.base_type),
668 e,
669 BASE_TYPE_INT) ||
670 TryTypedValue(kTokenFloatConstant,
671 IsFloat(e.type.base_type),
672 e,
673 BASE_TYPE_FLOAT) ||
674 TryTypedValue(kTokenStringConstant,
675 e.type.base_type == BASE_TYPE_STRING,
676 e,
677 BASE_TYPE_STRING)) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800678 } else {
679 Error("cannot parse value starting with: " + TokenToString(token_));
680 }
681}
682
683StructDef *Parser::LookupCreateStruct(const std::string &name) {
684 auto struct_def = structs_.Lookup(name);
685 if (!struct_def) {
686 // Rather than failing, we create a "pre declared" StructDef, due to
687 // circular references, and check for errors at the end of parsing.
688 struct_def = new StructDef();
689 structs_.Add(name, struct_def);
690 struct_def->name = name;
691 struct_def->predecl = true;
Wouter van Oortmerssenc2ba7fd2014-08-19 16:37:46 -0700692 struct_def->defined_namespace = namespaces_.back();
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800693 }
694 return struct_def;
695}
696
697void Parser::ParseEnum(bool is_union) {
698 std::string dc = doc_comment_;
699 Next();
700 std::string name = attribute_;
701 Expect(kTokenIdentifier);
702 auto &enum_def = *new EnumDef();
703 enum_def.name = name;
704 enum_def.doc_comment = dc;
705 enum_def.is_union = is_union;
706 if (enums_.Add(name, &enum_def)) Error("enum already exists: " + name);
707 if (is_union) {
708 enum_def.underlying_type.base_type = BASE_TYPE_UTYPE;
709 enum_def.underlying_type.enum_def = &enum_def;
Wouter van Oortmerssena5f50012014-07-02 12:01:21 -0700710 } else {
711 // Give specialized error message, since this type spec used to
712 // be optional in the first FlatBuffers release.
713 if (!IsNext(':')) Error("must specify the underlying integer type for this"
714 " enum (e.g. \': short\', which was the default).");
715 // Specify the integer type underlying this enum.
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800716 ParseType(enum_def.underlying_type);
717 if (!IsInteger(enum_def.underlying_type.base_type))
718 Error("underlying enum type must be integral");
Wouter van Oortmerssen3fb6a862014-07-14 17:49:04 -0700719 // Make this type refer back to the enum it was derived from.
720 enum_def.underlying_type.enum_def = &enum_def;
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800721 }
722 ParseMetaData(enum_def);
723 Expect('{');
724 if (is_union) enum_def.vals.Add("NONE", new EnumVal("NONE", 0));
725 do {
726 std::string name = attribute_;
727 std::string dc = doc_comment_;
728 Expect(kTokenIdentifier);
729 auto prevsize = enum_def.vals.vec.size();
Wouter van Oortmerssen127d3502014-07-17 15:12:37 -0700730 auto value = enum_def.vals.vec.size()
731 ? enum_def.vals.vec.back()->value + 1
732 : 0;
733 auto &ev = *new EnumVal(name, value);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800734 if (enum_def.vals.Add(name, &ev))
735 Error("enum value already exists: " + name);
736 ev.doc_comment = dc;
737 if (is_union) {
738 ev.struct_def = LookupCreateStruct(name);
739 }
740 if (IsNext('=')) {
741 ev.value = atoi(attribute_.c_str());
742 Expect(kTokenIntegerConstant);
743 if (prevsize && enum_def.vals.vec[prevsize - 1]->value >= ev.value)
744 Error("enum values must be specified in ascending order");
745 }
Wouter van Oortmerssenc553b6b2014-08-21 15:17:45 -0700746 } while (IsNext(',') && token_ != '}');
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800747 Expect('}');
Wouter van Oortmerssen127d3502014-07-17 15:12:37 -0700748 if (enum_def.attributes.Lookup("bit_flags")) {
749 for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
750 ++it) {
751 if (static_cast<size_t>((*it)->value) >=
752 SizeOf(enum_def.underlying_type.base_type) * 8)
753 Error("bit flag out of range of underlying integral type");
Wouter van Oortmerssen9c3de1e2014-07-25 15:04:35 -0700754 (*it)->value = 1LL << (*it)->value;
Wouter van Oortmerssen127d3502014-07-17 15:12:37 -0700755 }
756 }
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800757}
758
759void Parser::ParseDecl() {
760 std::string dc = doc_comment_;
761 bool fixed = IsNext(kTokenStruct);
762 if (!fixed) Expect(kTokenTable);
763 std::string name = attribute_;
764 Expect(kTokenIdentifier);
765 auto &struct_def = *LookupCreateStruct(name);
766 if (!struct_def.predecl) Error("datatype already exists: " + name);
767 struct_def.predecl = false;
768 struct_def.name = name;
769 struct_def.doc_comment = dc;
770 struct_def.fixed = fixed;
771 // Move this struct to the back of the vector just in case it was predeclared,
772 // to preserve declartion order.
773 remove(structs_.vec.begin(), structs_.vec.end(), &struct_def);
774 structs_.vec.back() = &struct_def;
775 ParseMetaData(struct_def);
776 struct_def.sortbysize =
777 struct_def.attributes.Lookup("original_order") == nullptr && !fixed;
778 Expect('{');
779 while (token_ != '}') ParseField(struct_def);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800780 auto force_align = struct_def.attributes.Lookup("force_align");
781 if (fixed && force_align) {
782 auto align = static_cast<size_t>(atoi(force_align->constant.c_str()));
783 if (force_align->type.base_type != BASE_TYPE_INT ||
784 align < struct_def.minalign ||
785 align > 256 ||
786 align & (align - 1))
787 Error("force_align must be a power of two integer ranging from the"
788 "struct\'s natural alignment to 256");
789 struct_def.minalign = align;
790 }
Wouter van Oortmerssen65cfa182014-06-23 11:34:19 -0700791 struct_def.PadLastField(struct_def.minalign);
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700792 // Check if this is a table that has manual id assignments
793 auto &fields = struct_def.fields.vec;
794 if (!struct_def.fixed && fields.size()) {
Wouter van Oortmerssen7fcbe722014-07-08 16:35:14 -0700795 size_t num_id_fields = 0;
Wouter van Oortmerssen91401442014-07-07 17:34:23 -0700796 for (auto it = fields.begin(); it != fields.end(); ++it) {
797 if ((*it)->attributes.Lookup("id")) num_id_fields++;
798 }
799 // If any fields have ids..
800 if (num_id_fields) {
801 // Then all fields must have them.
802 if (num_id_fields != fields.size())
803 Error("either all fields or no fields must have an 'id' attribute");
804 // Simply sort by id, then the fields are the same as if no ids had
805 // been specified.
806 std::sort(fields.begin(), fields.end(),
807 [](const FieldDef *a, const FieldDef *b) -> bool {
808 auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str());
809 auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str());
810 return a_id < b_id;
811 });
812 // Verify we have a contiguous set, and reassign vtable offsets.
813 for (int i = 0; i < static_cast<int>(fields.size()); i++) {
814 if (i != atoi(fields[i]->attributes.Lookup("id")->constant.c_str()))
815 Error("field id\'s must be consecutive from 0, id " +
816 NumToString(i) + " missing or set twice");
817 fields[i]->value.offset = FieldIndexToOffset(static_cast<voffset_t>(i));
818 }
819 }
820 }
Wouter van Oortmerssen541b0672014-08-21 15:02:15 -0700821 // Check that no identifiers clash with auto generated fields.
822 // This is not an ideal situation, but should occur very infrequently,
823 // and allows us to keep using very readable names for type & length fields
824 // without inducing compile errors.
825 auto CheckClash = [&fields, &struct_def](const char *suffix,
826 BaseType basetype) {
827 auto len = strlen(suffix);
828 for (auto it = fields.begin(); it != fields.end(); ++it) {
829 auto &name = (*it)->name;
830 if (name.length() > len &&
831 name.compare(name.length() - len, len, suffix) == 0 &&
832 (*it)->value.type.base_type != BASE_TYPE_UTYPE) {
833 auto field = struct_def.fields.Lookup(
834 name.substr(0, name.length() - len));
835 if (field && field->value.type.base_type == basetype)
836 Error("Field " + name +
837 " would clash with generated functions for field " +
838 field->name);
839 }
840 }
841 };
842 CheckClash("_type", BASE_TYPE_UNION);
843 CheckClash("Type", BASE_TYPE_UNION);
844 CheckClash("_length", BASE_TYPE_VECTOR);
845 CheckClash("Length", BASE_TYPE_VECTOR);
Wouter van Oortmerssen65cfa182014-06-23 11:34:19 -0700846 Expect('}');
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800847}
848
849bool Parser::SetRootType(const char *name) {
850 root_struct_def = structs_.Lookup(name);
851 return root_struct_def != nullptr;
852}
853
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700854void Parser::MarkGenerated() {
855 // Since the Parser object retains definitions across files, we must
856 // ensure we only output code for definitions once, in the file they are first
857 // declared. This function marks all existing definitions as having already
858 // been generated.
859 for (auto it = enums_.vec.begin();
860 it != enums_.vec.end(); ++it) {
861 (*it)->generated = true;
862 }
863 for (auto it = structs_.vec.begin();
864 it != structs_.vec.end(); ++it) {
865 (*it)->generated = true;
866 }
867}
868
Wouter van Oortmerssene57b86b2014-09-11 17:13:21 -0700869bool Parser::Parse(const char *source, const char **include_paths) {
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800870 source_ = cursor_ = source;
871 line_ = 1;
872 error_.clear();
873 builder_.Clear();
874 try {
875 Next();
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700876 // Includes must come first:
877 while (IsNext(kTokenInclude)) {
878 auto name = attribute_;
879 Expect(kTokenStringConstant);
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700880 if (included_files_.find(name) == included_files_.end()) {
881 // We found an include file that we have not parsed yet.
882 // Load it and parse it.
883 std::string contents;
Wouter van Oortmerssene57b86b2014-09-11 17:13:21 -0700884 if (!include_paths) {
885 const char *current_directory[] = { "", nullptr };
886 include_paths = current_directory;
887 }
888 for (auto paths = include_paths; paths && *paths; paths++) {
889 auto filepath = flatbuffers::ConCatPathFileName(*paths, name);
890 if(LoadFile(filepath.c_str(), true, &contents)) break;
891 }
892 if (contents.empty())
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700893 Error("unable to load include file: " + name);
Wouter van Oortmerssene57b86b2014-09-11 17:13:21 -0700894 included_files_[name] = true;
895 if (!Parse(contents.c_str(), include_paths)) {
896 // Any errors, we're done.
897 return false;
898 }
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700899 // We do not want to output code for any included files:
900 MarkGenerated();
901 // This is the easiest way to continue this file after an include:
902 // instead of saving and restoring all the state, we simply start the
903 // file anew. This will cause it to encounter the same include statement
904 // again, but this time it will skip it, because it was entered into
905 // included_files_.
Wouter van Oortmerssene57b86b2014-09-11 17:13:21 -0700906 // This is recursive, but only go as deep as the number of include
907 // statements.
908 return Parse(source, include_paths);
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700909 }
910 Expect(';');
911 }
912 // Now parse all other kinds of declarations:
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800913 while (token_ != kTokenEof) {
914 if (token_ == kTokenNameSpace) {
915 Next();
Wouter van Oortmerssenc2ba7fd2014-08-19 16:37:46 -0700916 auto ns = new Namespace();
917 namespaces_.push_back(ns);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800918 for (;;) {
Wouter van Oortmerssenc2ba7fd2014-08-19 16:37:46 -0700919 ns->components.push_back(attribute_);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800920 Expect(kTokenIdentifier);
921 if (!IsNext('.')) break;
922 }
923 Expect(';');
924 } else if (token_ == '{') {
925 if (!root_struct_def) Error("no root type set to parse json with");
926 if (builder_.GetSize()) {
927 Error("cannot have more than one json object in a file");
928 }
Wouter van Oortmerssen09a29992014-09-04 16:31:44 -0700929 builder_.Finish(Offset<Table>(ParseTable(*root_struct_def)),
930 file_identifier_.length() ? file_identifier_.c_str() : nullptr);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800931 } else if (token_ == kTokenEnum) {
932 ParseEnum(false);
933 } else if (token_ == kTokenUnion) {
934 ParseEnum(true);
935 } else if (token_ == kTokenRootType) {
936 Next();
937 auto root_type = attribute_;
938 Expect(kTokenIdentifier);
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800939 if (!SetRootType(root_type.c_str()))
940 Error("unknown root type: " + root_type);
941 if (root_struct_def->fixed)
942 Error("root type must be a table");
Wouter van Oortmerssen5da7bda2014-07-31 15:11:03 -0700943 Expect(';');
944 } else if (token_ == kTokenFileIdentifier) {
945 Next();
946 file_identifier_ = attribute_;
947 Expect(kTokenStringConstant);
948 if (file_identifier_.length() !=
949 FlatBufferBuilder::kFileIdentifierLength)
950 Error("file_identifier must be exactly " +
951 NumToString(FlatBufferBuilder::kFileIdentifierLength) +
952 " characters");
953 Expect(';');
954 } else if (token_ == kTokenFileExtension) {
955 Next();
956 file_extension_ = attribute_;
957 Expect(kTokenStringConstant);
958 Expect(';');
Wouter van Oortmerssenbe894f02014-08-19 14:20:05 -0700959 } else if(token_ == kTokenInclude) {
960 Error("includes must come before declarations");
Wouter van Oortmerssen26a30732014-01-27 16:52:49 -0800961 } else {
962 ParseDecl();
963 }
964 }
965 for (auto it = structs_.vec.begin(); it != structs_.vec.end(); ++it) {
966 if ((*it)->predecl)
967 Error("type referenced but not defined: " + (*it)->name);
968 }
969 for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
970 auto &enum_def = **it;
971 if (enum_def.is_union) {
972 for (auto it = enum_def.vals.vec.begin();
973 it != enum_def.vals.vec.end();
974 ++it) {
975 auto &val = **it;
976 if (val.struct_def && val.struct_def->fixed)
977 Error("only tables can be union elements: " + val.name);
978 }
979 }
980 }
981 } catch (const std::string &msg) {
982 error_ = "line " + NumToString(line_) + ": " + msg;
983 return false;
984 }
985 assert(!struct_stack_.size());
986 return true;
987}
988
989} // namespace flatbuffers