From f325cce6fdfc0c85e081fe90c7267fb0e8c79878 Mon Sep 17 00:00:00 2001 From: Wouter van Oortmerssen Date: Fri, 16 Jun 2017 11:57:58 -0700 Subject: [PATCH] Initial support for parsing (and generating) Protobuf ASCII. Change-Id: I955b4b3eed27f26773d7dc0acceff13c88d1333d Tested: on Linux. --- include/flatbuffers/idl.h | 3 +++ src/idl_gen_text.cpp | 9 ++++++--- src/idl_parser.cpp | 13 +++++++++---- tests/test.cpp | 21 +++++++++++++++++++++ 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index ab0421fe5..68426c254 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -366,6 +366,7 @@ struct IDLOptions { bool skip_flatbuffers_import; std::string go_namespace; bool reexport_ts_modules; + bool protobuf_ascii_alike; // Possible options for the more general generator below. enum Language { @@ -411,6 +412,7 @@ struct IDLOptions { binary_schema_comments(false), skip_flatbuffers_import(false), reexport_ts_modules(true), + protobuf_ascii_alike(false), lang(IDLOptions::kJava), lang_to_generate(0) {} }; @@ -564,6 +566,7 @@ private: FieldDef **dest); FLATBUFFERS_CHECKED_ERROR ParseField(StructDef &struct_def); FLATBUFFERS_CHECKED_ERROR ParseString(Value &val); + FLATBUFFERS_CHECKED_ERROR ParseComma(); FLATBUFFERS_CHECKED_ERROR ParseAnyValue(Value &val, FieldDef *field, size_t parent_fieldn, const StructDef *parent_struct_def); diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp index 9d741e0ee..f68ae95fc 100644 --- a/src/idl_gen_text.cpp +++ b/src/idl_gen_text.cpp @@ -80,7 +80,7 @@ template bool PrintVector(const Vector &v, Type type, text += NewLine(opts); for (uoffset_t i = 0; i < v.size(); i++) { if (i) { - text += ","; + if (!opts.protobuf_ascii_alike) text += ","; text += NewLine(opts); } text.append(indent + Indent(opts), ' '); @@ -207,12 +207,15 @@ static bool GenStruct(const StructDef &struct_def, const Table *table, !fd.deprecated; if (is_present || output_anyway) { if (fieldout++) { - text += ","; + if (!opts.protobuf_ascii_alike) text += ","; } text += NewLine(opts); text.append(indent + Indent(opts), ' '); OutputIdentifier(fd.name, opts, _text); - text += ": "; + if (!opts.protobuf_ascii_alike || + (fd.value.type.base_type != BASE_TYPE_STRUCT && + fd.value.type.base_type != BASE_TYPE_VECTOR)) text += ":"; + text += " "; if (is_present) { switch (fd.value.type.base_type) { #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \ diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index ff21fae7f..b572b9fd2 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -758,6 +758,11 @@ CheckedError Parser::ParseString(Value &val) { return NoError(); } +CheckedError Parser::ParseComma() { + if (!opts.protobuf_ascii_alike) EXPECT(','); + return NoError(); +} + CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field, size_t parent_fieldn, const StructDef *parent_struct_def) { @@ -786,7 +791,7 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field, // Remember where we are in the source file, so we can come back here. auto backup = *static_cast(this); ECHECK(SkipAnyJsonValue()); // The table. - EXPECT(','); + ECHECK(ParseComma()); auto next_name = attribute_; if (Is(kTokenStringConstant)) { NEXT(); @@ -891,11 +896,11 @@ CheckedError Parser::ParseTableDelimiters(size_t &fieldn, } else { EXPECT(opts.strict_json ? kTokenStringConstant : kTokenIdentifier); } - EXPECT(':'); + if (!opts.protobuf_ascii_alike || !(Is('{') || Is('['))) EXPECT(':'); } ECHECK(body(name)); if (Is(terminator)) break; - EXPECT(','); + ECHECK(ParseComma()); } NEXT(); if (is_nested_vector && fieldn != struct_def->fields.vec.size()) { @@ -1056,7 +1061,7 @@ CheckedError Parser::ParseVectorDelimiters(size_t &count, ECHECK(body()); count++; if (Is(']')) break; - EXPECT(','); + ECHECK(ParseComma()); } NEXT(); return NoError(); diff --git a/tests/test.cpp b/tests/test.cpp index 2754d8a72..840114c17 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1540,6 +1540,26 @@ void ConformTest() { test_conform("enum E:byte { B, A }", "values differ for enum"); } +void ParseProtoBufAsciiTest() { + // We can put the parser in a mode where it will accept JSON that looks more + // like Protobuf ASCII, for users that have data in that format. + // This uses no "" for field names (which we already support by default, + // omits `,`, `:` before `{` and a couple of other features. + flatbuffers::Parser parser; + parser.opts.protobuf_ascii_alike = true; + TEST_EQ(parser.Parse( + "table S { B:int; } table T { A:[int]; C:S; } root_type T;"), true); + TEST_EQ(parser.Parse("{ A [1 2] C { B:2 }}"), true); + // Similarly, in text output, it should omit these. + std::string text; + auto ok = flatbuffers::GenerateText(parser, + parser.builder_.GetBufferPointer(), + &text); + TEST_EQ(ok, true); + TEST_EQ_STR(text.c_str(), + "{\n A [\n 1\n 2\n ]\n C {\n B: 2\n }\n}\n"); +} + void FlexBuffersTest() { flexbuffers::Builder slb(512, flexbuffers::BUILDER_FLAG_SHARE_KEYS_AND_STRINGS); @@ -1659,6 +1679,7 @@ int main(int /*argc*/, const char * /*argv*/[]) { UnknownFieldsTest(); ParseUnionTest(); ConformTest(); + ParseProtoBufAsciiTest(); FlexBuffersTest();