1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132 |
- /*
- * Copyright 2014 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <algorithm>
- #include "flatbuffers/flatbuffers.h"
- #include "flatbuffers/idl.h"
- #include "flatbuffers/util.h"
- namespace flatbuffers {
- const char *const kTypeNames[] = {
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) IDLTYPE,
- FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
- #undef FLATBUFFERS_TD
- nullptr
- };
- const char kTypeSizes[] = {
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) \
- sizeof(CTYPE),
- FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
- #undef FLATBUFFERS_TD
- };
- static void Error(const std::string &msg) {
- throw msg;
- }
- // Ensure that integer values we parse fit inside the declared integer type.
- static void CheckBitsFit(int64_t val, size_t bits) {
- auto mask = (1ll << bits) - 1; // Bits we allow to be used.
- if (bits < 64 &&
- (val & ~mask) != 0 && // Positive or unsigned.
- (val | mask) != -1) // Negative.
- Error("constant does not fit in a " + NumToString(bits) + "-bit field");
- }
- // atot: templated version of atoi/atof: convert a string to an instance of T.
- template<typename T> inline T atot(const char *s) {
- auto val = StringToInt(s);
- CheckBitsFit(val, sizeof(T) * 8);
- return (T)val;
- }
- template<> inline bool atot<bool>(const char *s) {
- return 0 != atoi(s);
- }
- template<> inline float atot<float>(const char *s) {
- return static_cast<float>(strtod(s, nullptr));
- }
- template<> inline double atot<double>(const char *s) {
- return strtod(s, nullptr);
- }
- template<> inline Offset<void> atot<Offset<void>>(const char *s) {
- return Offset<void>(atoi(s));
- }
- // Declare tokens we'll use. Single character tokens are represented by their
- // ascii character code (e.g. '{'), others above 256.
- #define FLATBUFFERS_GEN_TOKENS(TD) \
- TD(Eof, 256, "end of file") \
- TD(StringConstant, 257, "string constant") \
- TD(IntegerConstant, 258, "integer constant") \
- TD(FloatConstant, 259, "float constant") \
- TD(Identifier, 260, "identifier") \
- TD(Table, 261, "table") \
- TD(Struct, 262, "struct") \
- TD(Enum, 263, "enum") \
- TD(Union, 264, "union") \
- TD(NameSpace, 265, "namespace") \
- TD(RootType, 266, "root_type") \
- TD(FileIdentifier, 267, "file_identifier") \
- TD(FileExtension, 268, "file_extension") \
- TD(Include, 269, "include")
- #ifdef __GNUC__
- __extension__ // Stop GCC complaining about trailing comma with -Wpendantic.
- #endif
- enum {
- #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) kToken ## NAME = VALUE,
- FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
- #undef FLATBUFFERS_TOKEN
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) \
- kToken ## ENUM,
- FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
- #undef FLATBUFFERS_TD
- };
- static std::string TokenToString(int t) {
- static const char *tokens[] = {
- #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) STRING,
- FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
- #undef FLATBUFFERS_TOKEN
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) IDLTYPE,
- FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
- #undef FLATBUFFERS_TD
- };
- if (t < 256) { // A single ascii char token.
- std::string s;
- s.append(1, static_cast<char>(t));
- return s;
- } else { // Other tokens.
- return tokens[t - 256];
- }
- }
- // Parses exactly nibbles worth of hex digits into a number, or error.
- int64_t Parser::ParseHexNum(int nibbles) {
- for (int i = 0; i < nibbles; i++)
- if (!isxdigit(cursor_[i]))
- Error("escape code must be followed by " + NumToString(nibbles) +
- " hex digits");
- auto val = StringToInt(cursor_, 16);
- cursor_ += nibbles;
- return val;
- }
- void Parser::Next() {
- doc_comment_.clear();
- bool seen_newline = false;
- for (;;) {
- char c = *cursor_++;
- token_ = c;
- switch (c) {
- case '\0': cursor_--; token_ = kTokenEof; return;
- case ' ': case '\r': case '\t': break;
- case '\n': line_++; seen_newline = true; break;
- case '{': case '}': case '(': case ')': case '[': case ']': return;
- case ',': case ':': case ';': case '=': return;
- case '.':
- if(!isdigit(*cursor_)) return;
- Error("floating point constant can\'t start with \".\"");
- break;
- case '\"':
- attribute_ = "";
- while (*cursor_ != '\"') {
- if (*cursor_ < ' ' && *cursor_ >= 0)
- Error("illegal character in string constant");
- if (*cursor_ == '\\') {
- cursor_++;
- switch (*cursor_) {
- case 'n': attribute_ += '\n'; cursor_++; break;
- case 't': attribute_ += '\t'; cursor_++; break;
- case 'r': attribute_ += '\r'; cursor_++; break;
- case 'b': attribute_ += '\b'; cursor_++; break;
- case 'f': attribute_ += '\f'; cursor_++; break;
- case '\"': attribute_ += '\"'; cursor_++; break;
- case '\\': attribute_ += '\\'; cursor_++; break;
- case '/': attribute_ += '/'; cursor_++; break;
- case 'x': { // Not in the JSON standard
- cursor_++;
- attribute_ += static_cast<char>(ParseHexNum(2));
- break;
- }
- case 'u': {
- cursor_++;
- ToUTF8(static_cast<int>(ParseHexNum(4)), &attribute_);
- break;
- }
- default: Error("unknown escape code in string constant"); break;
- }
- } else { // printable chars + UTF-8 bytes
- attribute_ += *cursor_++;
- }
- }
- cursor_++;
- token_ = kTokenStringConstant;
- return;
- case '/':
- if (*cursor_ == '/') {
- const char *start = ++cursor_;
- while (*cursor_ && *cursor_ != '\n') cursor_++;
- if (*start == '/') { // documentation comment
- if (!seen_newline)
- Error("a documentation comment should be on a line on its own");
- doc_comment_.push_back(std::string(start + 1, cursor_));
- }
- break;
- }
- // fall thru
- default:
- if (isalpha(static_cast<unsigned char>(c))) {
- // Collect all chars of an identifier:
- const char *start = cursor_ - 1;
- while (isalnum(static_cast<unsigned char>(*cursor_)) ||
- *cursor_ == '_')
- cursor_++;
- attribute_.clear();
- attribute_.append(start, cursor_);
- // First, see if it is a type keyword from the table of types:
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) \
- if (attribute_ == IDLTYPE) { \
- token_ = kToken ## ENUM; \
- return; \
- }
- FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
- #undef FLATBUFFERS_TD
- // If it's a boolean constant keyword, turn those into integers,
- // which simplifies our logic downstream.
- if (attribute_ == "true" || attribute_ == "false") {
- attribute_ = NumToString(attribute_ == "true");
- token_ = kTokenIntegerConstant;
- return;
- }
- // Check for declaration keywords:
- if (attribute_ == "table") { token_ = kTokenTable; return; }
- if (attribute_ == "struct") { token_ = kTokenStruct; return; }
- if (attribute_ == "enum") { token_ = kTokenEnum; return; }
- if (attribute_ == "union") { token_ = kTokenUnion; return; }
- if (attribute_ == "namespace") { token_ = kTokenNameSpace; return; }
- if (attribute_ == "root_type") { token_ = kTokenRootType; return; }
- if (attribute_ == "include") { token_ = kTokenInclude; return; }
- if (attribute_ == "file_identifier") {
- token_ = kTokenFileIdentifier;
- return;
- }
- if (attribute_ == "file_extension") {
- token_ = kTokenFileExtension;
- return;
- }
- // If not, it is a user-defined identifier:
- token_ = kTokenIdentifier;
- return;
- } else if (isdigit(static_cast<unsigned char>(c)) || c == '-') {
- const char *start = cursor_ - 1;
- while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
- if (*cursor_ == '.') {
- cursor_++;
- while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
- // See if this float has a scientific notation suffix. Both JSON
- // and C++ (through strtod() we use) have the same format:
- if (*cursor_ == 'e' || *cursor_ == 'E') {
- cursor_++;
- if (*cursor_ == '+' || *cursor_ == '-') cursor_++;
- while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
- }
- token_ = kTokenFloatConstant;
- } else {
- token_ = kTokenIntegerConstant;
- }
- attribute_.clear();
- attribute_.append(start, cursor_);
- return;
- }
- std::string ch;
- ch = c;
- if (c < ' ' || c > '~') ch = "code: " + NumToString(c);
- Error("illegal character: " + ch);
- break;
- }
- }
- }
- // Check if a given token is next, if so, consume it as well.
- bool Parser::IsNext(int t) {
- bool isnext = t == token_;
- if (isnext) Next();
- return isnext;
- }
- // Expect a given token to be next, consume it, or error if not present.
- void Parser::Expect(int t) {
- if (t != token_) {
- Error("expecting: " + TokenToString(t) + " instead got: " +
- TokenToString(token_));
- }
- Next();
- }
- void Parser::ParseTypeIdent(Type &type) {
- auto enum_def = enums_.Lookup(attribute_);
- if (enum_def) {
- type = enum_def->underlying_type;
- if (enum_def->is_union) type.base_type = BASE_TYPE_UNION;
- } else {
- type.base_type = BASE_TYPE_STRUCT;
- type.struct_def = LookupCreateStruct(attribute_);
- }
- }
- // Parse any IDL type.
- void Parser::ParseType(Type &type) {
- if (token_ >= kTokenBOOL && token_ <= kTokenSTRING) {
- type.base_type = static_cast<BaseType>(token_ - kTokenNONE);
- } else {
- if (token_ == kTokenIdentifier) {
- ParseTypeIdent(type);
- } else if (token_ == '[') {
- Next();
- Type subtype;
- ParseType(subtype);
- if (subtype.base_type == BASE_TYPE_VECTOR) {
- // We could support this, but it will complicate things, and it's
- // easier to work around with a struct around the inner vector.
- Error("nested vector types not supported (wrap in table first).");
- }
- if (subtype.base_type == BASE_TYPE_UNION) {
- // We could support this if we stored a struct of 2 elements per
- // union element.
- Error("vector of union types not supported (wrap in table first).");
- }
- type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def);
- type.element = subtype.base_type;
- Expect(']');
- return;
- } else {
- Error("illegal type syntax");
- }
- }
- Next();
- }
- FieldDef &Parser::AddField(StructDef &struct_def,
- const std::string &name,
- const Type &type) {
- auto &field = *new FieldDef();
- field.value.offset =
- FieldIndexToOffset(static_cast<voffset_t>(struct_def.fields.vec.size()));
- field.name = name;
- field.value.type = type;
- if (struct_def.fixed) { // statically compute the field offset
- auto size = InlineSize(type);
- auto alignment = InlineAlignment(type);
- // structs_ need to have a predictable format, so we need to align to
- // the largest scalar
- struct_def.minalign = std::max(struct_def.minalign, alignment);
- struct_def.PadLastField(alignment);
- field.value.offset = static_cast<voffset_t>(struct_def.bytesize);
- struct_def.bytesize += size;
- }
- if (struct_def.fields.Add(name, &field))
- Error("field already exists: " + name);
- return field;
- }
- void Parser::ParseField(StructDef &struct_def) {
- std::string name = attribute_;
- std::vector<std::string> dc = doc_comment_;
- Expect(kTokenIdentifier);
- Expect(':');
- Type type;
- ParseType(type);
- if (struct_def.fixed && !IsScalar(type.base_type) && !IsStruct(type))
- Error("structs_ may contain only scalar or struct fields");
- FieldDef *typefield = nullptr;
- if (type.base_type == BASE_TYPE_UNION) {
- // For union fields, add a second auto-generated field to hold the type,
- // with _type appended as the name.
- typefield = &AddField(struct_def, name + "_type",
- type.enum_def->underlying_type);
- }
- auto &field = AddField(struct_def, name, type);
- if (token_ == '=') {
- Next();
- if (!IsScalar(type.base_type))
- Error("default values currently only supported for scalars");
- ParseSingleValue(field.value);
- }
- if (type.enum_def &&
- IsScalar(type.base_type) &&
- !struct_def.fixed &&
- !type.enum_def->attributes.Lookup("bit_flags") &&
- !type.enum_def->ReverseLookup(static_cast<int>(
- StringToInt(field.value.constant.c_str()))))
- Error("enum " + type.enum_def->name +
- " does not have a declaration for this field\'s default of " +
- field.value.constant);
- field.doc_comment = dc;
- ParseMetaData(field);
- field.deprecated = field.attributes.Lookup("deprecated") != nullptr;
- if (field.deprecated && struct_def.fixed)
- Error("can't deprecate fields in a struct");
- field.required = field.attributes.Lookup("required") != nullptr;
- if (field.required && (struct_def.fixed ||
- IsScalar(field.value.type.base_type)))
- Error("only non-scalar fields in tables may be 'required'");
- auto nested = field.attributes.Lookup("nested_flatbuffer");
- if (nested) {
- if (nested->type.base_type != BASE_TYPE_STRING)
- Error("nested_flatbuffer attribute must be a string (the root type)");
- if (field.value.type.base_type != BASE_TYPE_VECTOR ||
- field.value.type.element != BASE_TYPE_UCHAR)
- Error("nested_flatbuffer attribute may only apply to a vector of ubyte");
- // This will cause an error if the root type of the nested flatbuffer
- // wasn't defined elsewhere.
- LookupCreateStruct(nested->constant);
- }
- if (typefield) {
- // If this field is a union, and it has a manually assigned id,
- // the automatically added type field should have an id as well (of N - 1).
- auto attr = field.attributes.Lookup("id");
- if (attr) {
- auto id = atoi(attr->constant.c_str());
- auto val = new Value();
- val->type = attr->type;
- val->constant = NumToString(id - 1);
- typefield->attributes.Add("id", val);
- }
- }
- Expect(';');
- }
- void Parser::ParseAnyValue(Value &val, FieldDef *field) {
- switch (val.type.base_type) {
- case BASE_TYPE_UNION: {
- assert(field);
- if (!field_stack_.size() ||
- field_stack_.back().second->value.type.base_type != BASE_TYPE_UTYPE)
- Error("missing type field before this union value: " + field->name);
- auto enum_idx = atot<unsigned char>(
- field_stack_.back().first.constant.c_str());
- auto enum_val = val.type.enum_def->ReverseLookup(enum_idx);
- if (!enum_val) Error("illegal type id for: " + field->name);
- val.constant = NumToString(ParseTable(*enum_val->struct_def));
- break;
- }
- case BASE_TYPE_STRUCT:
- val.constant = NumToString(ParseTable(*val.type.struct_def));
- break;
- case BASE_TYPE_STRING: {
- auto s = attribute_;
- Expect(kTokenStringConstant);
- val.constant = NumToString(builder_.CreateString(s).o);
- break;
- }
- case BASE_TYPE_VECTOR: {
- Expect('[');
- val.constant = NumToString(ParseVector(val.type.VectorType()));
- break;
- }
- default:
- ParseSingleValue(val);
- break;
- }
- }
- void Parser::SerializeStruct(const StructDef &struct_def, const Value &val) {
- auto off = atot<uoffset_t>(val.constant.c_str());
- assert(struct_stack_.size() - off == struct_def.bytesize);
- builder_.Align(struct_def.minalign);
- builder_.PushBytes(&struct_stack_[off], struct_def.bytesize);
- struct_stack_.resize(struct_stack_.size() - struct_def.bytesize);
- builder_.AddStructOffset(val.offset, builder_.GetSize());
- }
- uoffset_t Parser::ParseTable(const StructDef &struct_def) {
- Expect('{');
- size_t fieldn = 0;
- if (!IsNext('}')) for (;;) {
- std::string name = attribute_;
- if (!IsNext(kTokenStringConstant)) Expect(kTokenIdentifier);
- auto field = struct_def.fields.Lookup(name);
- if (!field) Error("unknown field: " + name);
- if (struct_def.fixed && (fieldn >= struct_def.fields.vec.size()
- || struct_def.fields.vec[fieldn] != field)) {
- Error("struct field appearing out of order: " + name);
- }
- Expect(':');
- Value val = field->value;
- ParseAnyValue(val, field);
- field_stack_.push_back(std::make_pair(val, field));
- fieldn++;
- if (IsNext('}')) break;
- Expect(',');
- }
- for (auto it = field_stack_.rbegin();
- it != field_stack_.rbegin() + fieldn; ++it) {
- if (it->second->used)
- Error("field set more than once: " + it->second->name);
- it->second->used = true;
- }
- for (auto it = field_stack_.rbegin();
- it != field_stack_.rbegin() + fieldn; ++it) {
- it->second->used = false;
- }
- if (struct_def.fixed && fieldn != struct_def.fields.vec.size())
- Error("incomplete struct initialization: " + struct_def.name);
- auto start = struct_def.fixed
- ? builder_.StartStruct(struct_def.minalign)
- : builder_.StartTable();
- for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
- size;
- size /= 2) {
- // Go through elements in reverse, since we're building the data backwards.
- for (auto it = field_stack_.rbegin();
- it != field_stack_.rbegin() + fieldn; ++it) {
- auto &value = it->first;
- auto field = it->second;
- if (!struct_def.sortbysize || size == SizeOf(value.type.base_type)) {
- switch (value.type.base_type) {
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) \
- case BASE_TYPE_ ## ENUM: \
- builder_.Pad(field->padding); \
- if (struct_def.fixed) { \
- builder_.PushElement(atot<CTYPE>(value.constant.c_str())); \
- } else { \
- builder_.AddElement(value.offset, \
- atot<CTYPE>( value.constant.c_str()), \
- atot<CTYPE>(field->value.constant.c_str())); \
- } \
- break;
- FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
- #undef FLATBUFFERS_TD
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) \
- case BASE_TYPE_ ## ENUM: \
- builder_.Pad(field->padding); \
- if (IsStruct(field->value.type)) { \
- SerializeStruct(*field->value.type.struct_def, value); \
- } else { \
- builder_.AddOffset(value.offset, \
- atot<CTYPE>(value.constant.c_str())); \
- } \
- break;
- FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD);
- #undef FLATBUFFERS_TD
- }
- }
- }
- }
- for (size_t i = 0; i < fieldn; i++) field_stack_.pop_back();
- if (struct_def.fixed) {
- builder_.ClearOffsets();
- builder_.EndStruct();
- // Temporarily store this struct in a side buffer, since this data has to
- // be stored in-line later in the parent object.
- auto off = struct_stack_.size();
- struct_stack_.insert(struct_stack_.end(),
- builder_.GetBufferPointer(),
- builder_.GetBufferPointer() + struct_def.bytesize);
- builder_.PopBytes(struct_def.bytesize);
- return static_cast<uoffset_t>(off);
- } else {
- return builder_.EndTable(
- start,
- static_cast<voffset_t>(struct_def.fields.vec.size()));
- }
- }
- uoffset_t Parser::ParseVector(const Type &type) {
- int count = 0;
- if (token_ != ']') for (;;) {
- Value val;
- val.type = type;
- ParseAnyValue(val, NULL);
- #ifdef WP8
- field_stack_.push_back(std::make_pair(val, (FieldDef *)nullptr));
- #else
- field_stack_.push_back(std::make_pair(val, nullptr));
- #endif
- count++;
- if (token_ == ']') break;
- Expect(',');
- }
- Next();
- builder_.StartVector(count * InlineSize(type) / InlineAlignment(type),
- InlineAlignment(type));
- for (int i = 0; i < count; i++) {
- // start at the back, since we're building the data backwards.
- auto &val = field_stack_.back().first;
- switch (val.type.base_type) {
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE) \
- case BASE_TYPE_ ## ENUM: \
- if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \
- else builder_.PushElement(atot<CTYPE>(val.constant.c_str())); \
- break;
- FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
- #undef FLATBUFFERS_TD
- }
- field_stack_.pop_back();
- }
- builder_.ClearOffsets();
- return builder_.EndVector(count);
- }
- void Parser::ParseMetaData(Definition &def) {
- if (IsNext('(')) {
- for (;;) {
- auto name = attribute_;
- Expect(kTokenIdentifier);
- auto e = new Value();
- def.attributes.Add(name, e);
- if (IsNext(':')) {
- ParseSingleValue(*e);
- }
- if (IsNext(')')) break;
- Expect(',');
- }
- }
- }
- bool Parser::TryTypedValue(int dtoken,
- bool check,
- Value &e,
- BaseType req) {
- bool match = dtoken == token_;
- if (match) {
- e.constant = attribute_;
- if (!check) {
- if (e.type.base_type == BASE_TYPE_NONE) {
- e.type.base_type = req;
- } else {
- Error(std::string("type mismatch: expecting: ") +
- kTypeNames[e.type.base_type] +
- ", found: " +
- kTypeNames[req]);
- }
- }
- Next();
- }
- return match;
- }
- int64_t Parser::ParseIntegerFromString(Type &type) {
- int64_t result = 0;
- // Parse one or more enum identifiers, separated by spaces.
- const char *next = attribute_.c_str();
- do {
- const char *divider = strchr(next, ' ');
- std::string word;
- if (divider) {
- word = std::string(next, divider);
- next = divider + strspn(divider, " ");
- } else {
- word = next;
- next += word.length();
- }
- if (type.enum_def) { // The field has an enum type
- auto enum_val = type.enum_def->vals.Lookup(word);
- if (!enum_val)
- Error("unknown enum value: " + word +
- ", for enum: " + type.enum_def->name);
- result |= enum_val->value;
- } else { // No enum type, probably integral field.
- if (!IsInteger(type.base_type))
- Error("not a valid value for this field: " + word);
- // TODO: could check if its a valid number constant here.
- const char *dot = strchr(word.c_str(), '.');
- if (!dot) Error("enum values need to be qualified by an enum type");
- std::string enum_def_str(word.c_str(), dot);
- std::string enum_val_str(dot + 1, word.c_str() + word.length());
- auto enum_def = enums_.Lookup(enum_def_str);
- if (!enum_def) Error("unknown enum: " + enum_def_str);
- auto enum_val = enum_def->vals.Lookup(enum_val_str);
- if (!enum_val) Error("unknown enum value: " + enum_val_str);
- result |= enum_val->value;
- }
- } while(*next);
- return result;
- }
- void Parser::ParseSingleValue(Value &e) {
- // First check if this could be a string/identifier enum value:
- if (e.type.base_type != BASE_TYPE_STRING &&
- e.type.base_type != BASE_TYPE_NONE &&
- (token_ == kTokenIdentifier || token_ == kTokenStringConstant)) {
- e.constant = NumToString(ParseIntegerFromString(e.type));
- Next();
- } else if (TryTypedValue(kTokenIntegerConstant,
- IsScalar(e.type.base_type),
- e,
- BASE_TYPE_INT) ||
- TryTypedValue(kTokenFloatConstant,
- IsFloat(e.type.base_type),
- e,
- BASE_TYPE_FLOAT) ||
- TryTypedValue(kTokenStringConstant,
- e.type.base_type == BASE_TYPE_STRING,
- e,
- BASE_TYPE_STRING)) {
- } else {
- Error("cannot parse value starting with: " + TokenToString(token_));
- }
- }
- StructDef *Parser::LookupCreateStruct(const std::string &name) {
- auto struct_def = structs_.Lookup(name);
- if (!struct_def) {
- // Rather than failing, we create a "pre declared" StructDef, due to
- // circular references, and check for errors at the end of parsing.
- struct_def = new StructDef();
- structs_.Add(name, struct_def);
- struct_def->name = name;
- struct_def->predecl = true;
- struct_def->defined_namespace = namespaces_.back();
- }
- return struct_def;
- }
- void Parser::ParseEnum(bool is_union) {
- std::vector<std::string> dc = doc_comment_;
- Next();
- std::string name = attribute_;
- Expect(kTokenIdentifier);
- auto &enum_def = *new EnumDef();
- enum_def.name = name;
- enum_def.doc_comment = dc;
- enum_def.is_union = is_union;
- enum_def.defined_namespace = namespaces_.back();
- if (enums_.Add(name, &enum_def)) Error("enum already exists: " + name);
- if (is_union) {
- enum_def.underlying_type.base_type = BASE_TYPE_UTYPE;
- enum_def.underlying_type.enum_def = &enum_def;
- } else {
- if (proto_mode_) {
- enum_def.underlying_type.base_type = BASE_TYPE_SHORT;
- } else {
- // Give specialized error message, since this type spec used to
- // be optional in the first FlatBuffers release.
- if (!IsNext(':')) Error("must specify the underlying integer type for this"
- " enum (e.g. \': short\', which was the default).");
- // Specify the integer type underlying this enum.
- ParseType(enum_def.underlying_type);
- if (!IsInteger(enum_def.underlying_type.base_type))
- Error("underlying enum type must be integral");
- }
- // Make this type refer back to the enum it was derived from.
- enum_def.underlying_type.enum_def = &enum_def;
- }
- ParseMetaData(enum_def);
- Expect('{');
- if (is_union) enum_def.vals.Add("NONE", new EnumVal("NONE", 0));
- do {
- name = attribute_;
- dc = doc_comment_;
- Expect(kTokenIdentifier);
- auto prevsize = enum_def.vals.vec.size();
- auto value = enum_def.vals.vec.size()
- ? enum_def.vals.vec.back()->value + 1
- : 0;
- auto &ev = *new EnumVal(name, value);
- if (enum_def.vals.Add(name, &ev))
- Error("enum value already exists: " + name);
- ev.doc_comment = dc;
- if (is_union) {
- ev.struct_def = LookupCreateStruct(name);
- }
- if (IsNext('=')) {
- ev.value = atoi(attribute_.c_str());
- Expect(kTokenIntegerConstant);
- if (prevsize && enum_def.vals.vec[prevsize - 1]->value >= ev.value)
- Error("enum values must be specified in ascending order");
- }
- } while (IsNext(proto_mode_ ? ';' : ',') && token_ != '}');
- Expect('}');
- if (enum_def.attributes.Lookup("bit_flags")) {
- for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
- ++it) {
- if (static_cast<size_t>((*it)->value) >=
- SizeOf(enum_def.underlying_type.base_type) * 8)
- Error("bit flag out of range of underlying integral type");
- (*it)->value = 1LL << (*it)->value;
- }
- }
- }
- StructDef &Parser::StartStruct() {
- std::string name = attribute_;
- Expect(kTokenIdentifier);
- auto &struct_def = *LookupCreateStruct(name);
- if (!struct_def.predecl) Error("datatype already exists: " + name);
- struct_def.predecl = false;
- struct_def.name = name;
- // Move this struct to the back of the vector just in case it was predeclared,
- // to preserve declaration order.
- remove(structs_.vec.begin(), structs_.vec.end(), &struct_def);
- structs_.vec.back() = &struct_def;
- return struct_def;
- }
- void Parser::ParseDecl() {
- std::vector<std::string> dc = doc_comment_;
- bool fixed = IsNext(kTokenStruct);
- if (!fixed) Expect(kTokenTable);
- auto &struct_def = StartStruct();
- struct_def.doc_comment = dc;
- struct_def.fixed = fixed;
- ParseMetaData(struct_def);
- struct_def.sortbysize =
- struct_def.attributes.Lookup("original_order") == nullptr && !fixed;
- Expect('{');
- while (token_ != '}') ParseField(struct_def);
- auto force_align = struct_def.attributes.Lookup("force_align");
- if (fixed && force_align) {
- auto align = static_cast<size_t>(atoi(force_align->constant.c_str()));
- if (force_align->type.base_type != BASE_TYPE_INT ||
- align < struct_def.minalign ||
- align > 256 ||
- align & (align - 1))
- Error("force_align must be a power of two integer ranging from the"
- "struct\'s natural alignment to 256");
- struct_def.minalign = align;
- }
- struct_def.PadLastField(struct_def.minalign);
- // Check if this is a table that has manual id assignments
- auto &fields = struct_def.fields.vec;
- if (!struct_def.fixed && fields.size()) {
- size_t num_id_fields = 0;
- for (auto it = fields.begin(); it != fields.end(); ++it) {
- if ((*it)->attributes.Lookup("id")) num_id_fields++;
- }
- // If any fields have ids..
- if (num_id_fields) {
- // Then all fields must have them.
- if (num_id_fields != fields.size())
- Error("either all fields or no fields must have an 'id' attribute");
- // Simply sort by id, then the fields are the same as if no ids had
- // been specified.
- std::sort(fields.begin(), fields.end(),
- [](const FieldDef *a, const FieldDef *b) -> bool {
- auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str());
- auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str());
- return a_id < b_id;
- });
- // Verify we have a contiguous set, and reassign vtable offsets.
- for (int i = 0; i < static_cast<int>(fields.size()); i++) {
- if (i != atoi(fields[i]->attributes.Lookup("id")->constant.c_str()))
- Error("field id\'s must be consecutive from 0, id " +
- NumToString(i) + " missing or set twice");
- fields[i]->value.offset = FieldIndexToOffset(static_cast<voffset_t>(i));
- }
- }
- }
- // Check that no identifiers clash with auto generated fields.
- // This is not an ideal situation, but should occur very infrequently,
- // and allows us to keep using very readable names for type & length fields
- // without inducing compile errors.
- auto CheckClash = [&fields, &struct_def](const char *suffix,
- BaseType basetype) {
- auto len = strlen(suffix);
- for (auto it = fields.begin(); it != fields.end(); ++it) {
- auto &name = (*it)->name;
- if (name.length() > len &&
- name.compare(name.length() - len, len, suffix) == 0 &&
- (*it)->value.type.base_type != BASE_TYPE_UTYPE) {
- auto field = struct_def.fields.Lookup(
- name.substr(0, name.length() - len));
- if (field && field->value.type.base_type == basetype)
- Error("Field " + name +
- " would clash with generated functions for field " +
- field->name);
- }
- }
- };
- CheckClash("_type", BASE_TYPE_UNION);
- CheckClash("Type", BASE_TYPE_UNION);
- CheckClash("_length", BASE_TYPE_VECTOR);
- CheckClash("Length", BASE_TYPE_VECTOR);
- Expect('}');
- }
- bool Parser::SetRootType(const char *name) {
- root_struct_def = structs_.Lookup(name);
- return root_struct_def != nullptr;
- }
- void Parser::MarkGenerated() {
- // Since the Parser object retains definitions across files, we must
- // ensure we only output code for definitions once, in the file they are first
- // declared. This function marks all existing definitions as having already
- // been generated.
- for (auto it = enums_.vec.begin();
- it != enums_.vec.end(); ++it) {
- (*it)->generated = true;
- }
- for (auto it = structs_.vec.begin();
- it != structs_.vec.end(); ++it) {
- (*it)->generated = true;
- }
- }
- void Parser::ParseNamespace() {
- Next();
- auto ns = new Namespace();
- namespaces_.push_back(ns);
- for (;;) {
- ns->components.push_back(attribute_);
- Expect(kTokenIdentifier);
- if (!IsNext('.')) break;
- }
- Expect(';');
- }
- // Best effort parsing of .proto declarations, with the aim to turn them
- // in the closest corresponding FlatBuffer equivalent.
- // We parse everything as identifiers instead of keywords, since we don't
- // want protobuf keywords to become invalid identifiers in FlatBuffers.
- void Parser::ParseProtoDecl() {
- if (attribute_ == "package") {
- // These are identical in syntax to FlatBuffer's namespace decl.
- ParseNamespace();
- } else if (attribute_ == "message") {
- Next();
- auto &struct_def = StartStruct();
- Expect('{');
- while (token_ != '}') {
- // Parse the qualifier.
- bool required = false;
- bool repeated = false;
- if (attribute_ == "optional") {
- // This is the default.
- } else if (attribute_ == "required") {
- required = true;
- } else if (attribute_ == "repeated") {
- repeated = true;
- } else {
- Error("expecting optional/required/repeated, got: " + attribute_);
- }
- Type type = ParseTypeFromProtoType();
- // Repeated elements get mapped to a vector.
- if (repeated) {
- type.element = type.base_type;
- type.base_type = BASE_TYPE_VECTOR;
- }
- std::string name = attribute_;
- Expect(kTokenIdentifier);
- // Parse the field id. Since we're just translating schemas, not
- // any kind of binary compatibility, we can safely ignore these, and
- // assign our own.
- Expect('=');
- Expect(kTokenIntegerConstant);
- auto &field = AddField(struct_def, name, type);
- field.required = required;
- // See if there's a default specified.
- if (IsNext('[')) {
- if (attribute_ != "default") Error("\'default\' expected");
- Next();
- Expect('=');
- field.value.constant = attribute_;
- Next();
- Expect(']');
- }
- Expect(';');
- }
- Next();
- } else if (attribute_ == "enum") {
- // These are almost the same, just with different terminator:
- ParseEnum(false);
- } else if (attribute_ == "import") {
- Next();
- included_files_[attribute_] = true;
- Expect(kTokenStringConstant);
- Expect(';');
- } else if (attribute_ == "option") { // Skip these.
- Next();
- Expect(kTokenIdentifier);
- Expect('=');
- Next(); // Any single token.
- Expect(';');
- } else {
- Error("don\'t know how to parse .proto declaration starting with " +
- attribute_);
- }
- }
- // Parse a protobuf type, and map it to the corresponding FlatBuffer one.
- Type Parser::ParseTypeFromProtoType() {
- Expect(kTokenIdentifier);
- struct type_lookup { const char *proto_type; BaseType fb_type; };
- static type_lookup lookup[] = {
- { "float", BASE_TYPE_FLOAT }, { "double", BASE_TYPE_DOUBLE },
- { "int32", BASE_TYPE_INT }, { "int64", BASE_TYPE_LONG },
- { "uint32", BASE_TYPE_UINT }, { "uint64", BASE_TYPE_ULONG },
- { "sint32", BASE_TYPE_INT }, { "sint64", BASE_TYPE_LONG },
- { "fixed32", BASE_TYPE_UINT }, { "fixed64", BASE_TYPE_ULONG },
- { "sfixed32", BASE_TYPE_INT }, { "sfixed64", BASE_TYPE_LONG },
- { "bool", BASE_TYPE_BOOL },
- { "string", BASE_TYPE_STRING },
- { "bytes", BASE_TYPE_STRING },
- { nullptr, BASE_TYPE_NONE }
- };
- Type type;
- for (auto tl = lookup; tl->proto_type; tl++) {
- if (attribute_ == tl->proto_type) {
- type.base_type = tl->fb_type;
- Next();
- return type;
- }
- }
- ParseTypeIdent(type);
- Expect(kTokenIdentifier);
- return type;
- }
- bool Parser::Parse(const char *source, const char **include_paths,
- const char *source_filename) {
- if (source_filename) included_files_[source_filename] = true;
- source_ = cursor_ = source;
- line_ = 1;
- error_.clear();
- builder_.Clear();
- try {
- Next();
- // Includes must come first:
- while (IsNext(kTokenInclude)) {
- auto name = attribute_;
- Expect(kTokenStringConstant);
- if (included_files_.find(name) == included_files_.end()) {
- // We found an include file that we have not parsed yet.
- // Load it and parse it.
- std::string contents;
- if (!include_paths) {
- const char *current_directory[] = { "", nullptr };
- include_paths = current_directory;
- }
- for (auto paths = include_paths; paths && *paths; paths++) {
- auto filepath = flatbuffers::ConCatPathFileName(*paths, name);
- if(LoadFile(filepath.c_str(), true, &contents)) break;
- }
- if (contents.empty())
- Error("unable to load include file: " + name);
- included_files_[name] = true;
- if (!Parse(contents.c_str(), include_paths)) {
- // Any errors, we're done.
- return false;
- }
- // We do not want to output code for any included files:
- MarkGenerated();
- // This is the easiest way to continue this file after an include:
- // instead of saving and restoring all the state, we simply start the
- // file anew. This will cause it to encounter the same include statement
- // again, but this time it will skip it, because it was entered into
- // included_files_.
- // This is recursive, but only go as deep as the number of include
- // statements.
- return Parse(source, include_paths, source_filename);
- }
- Expect(';');
- }
- // Now parse all other kinds of declarations:
- while (token_ != kTokenEof) {
- if (proto_mode_) {
- ParseProtoDecl();
- } else if (token_ == kTokenNameSpace) {
- ParseNamespace();
- } else if (token_ == '{') {
- if (!root_struct_def) Error("no root type set to parse json with");
- if (builder_.GetSize()) {
- Error("cannot have more than one json object in a file");
- }
- builder_.Finish(Offset<Table>(ParseTable(*root_struct_def)),
- file_identifier_.length() ? file_identifier_.c_str() : nullptr);
- } else if (token_ == kTokenEnum) {
- ParseEnum(false);
- } else if (token_ == kTokenUnion) {
- ParseEnum(true);
- } else if (token_ == kTokenRootType) {
- Next();
- auto root_type = attribute_;
- Expect(kTokenIdentifier);
- if (!SetRootType(root_type.c_str()))
- Error("unknown root type: " + root_type);
- if (root_struct_def->fixed)
- Error("root type must be a table");
- Expect(';');
- } else if (token_ == kTokenFileIdentifier) {
- Next();
- file_identifier_ = attribute_;
- Expect(kTokenStringConstant);
- if (file_identifier_.length() !=
- FlatBufferBuilder::kFileIdentifierLength)
- Error("file_identifier must be exactly " +
- NumToString(FlatBufferBuilder::kFileIdentifierLength) +
- " characters");
- Expect(';');
- } else if (token_ == kTokenFileExtension) {
- Next();
- file_extension_ = attribute_;
- Expect(kTokenStringConstant);
- Expect(';');
- } else if(token_ == kTokenInclude) {
- Error("includes must come before declarations");
- } else {
- ParseDecl();
- }
- }
- for (auto it = structs_.vec.begin(); it != structs_.vec.end(); ++it) {
- if ((*it)->predecl)
- Error("type referenced but not defined: " + (*it)->name);
- }
- for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
- auto &enum_def = **it;
- if (enum_def.is_union) {
- for (auto it2 = enum_def.vals.vec.begin();
- it2 != enum_def.vals.vec.end();
- ++it2) {
- auto &val = **it2;
- if (val.struct_def && val.struct_def->fixed)
- Error("only tables can be union elements: " + val.name);
- }
- }
- }
- } catch (const std::string &msg) {
- error_ = source_filename ? AbsolutePath(source_filename) : "";
- #ifdef _WIN32
- error_ += "(" + NumToString(line_) + ")"; // MSVC alike
- #else
- if (source_filename) error_ += ":";
- error_ += NumToString(line_) + ":0"; // gcc alike
- #endif
- error_ += ": error: " + msg;
- return false;
- }
- assert(!struct_stack_.size());
- return true;
- }
- } // namespace flatbuffers
|