Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 29 additions & 7 deletions compiler/back_end/cpp/generated_code_templates
Original file line number Diff line number Diff line change
Expand Up @@ -296,11 +296,18 @@ ${write_fields}
// Avoid unused variable warnings for empty structures:
(void)emboss_reserved_local_wrote_field;
if (emboss_reserved_local_options.multiline()) {
if (emboss_reserved_local_wrote_field &&
emboss_reserved_local_options.json()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can delete the && ...json() here and then lines 348-351 entirely, I believe.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would break non-JSON multiline output, suggestion?

emboss_reserved_local_stream->Write("\n");
}
emboss_reserved_local_stream->Write(
emboss_reserved_local_options.current_indent());
emboss_reserved_local_stream->Write("}");
} else {
emboss_reserved_local_stream->Write(" }");
if (!emboss_reserved_local_options.json()) {
emboss_reserved_local_stream->Write(" ");
}
emboss_reserved_local_stream->Write("}");
}
}

Expand All @@ -324,20 +331,35 @@ ${write_fields}
// they are not `Ok()` overall, since submembers may still be `Ok()`.
if (!emboss_reserved_local_field_options.allow_partial_output() ||
${field_name}().IsAggregate() || ${field_name}().Ok()) {
if (emboss_reserved_local_wrote_field) {
if (emboss_reserved_local_field_options.json() ||
!emboss_reserved_local_field_options.multiline()) {
emboss_reserved_local_stream->Write(",");
}
}
if (emboss_reserved_local_field_options.multiline()) {
// In JSON mode, each field goes on its own line, so emit the newline
// that separates this field from the previous one. (In non-JSON
// mode the previous field already wrote its own trailing newline.)
if (emboss_reserved_local_wrote_field &&
emboss_reserved_local_field_options.json()) {
emboss_reserved_local_stream->Write("\n");
}
emboss_reserved_local_stream->Write(
emboss_reserved_local_field_options.current_indent());
} else {
if (emboss_reserved_local_wrote_field) {
emboss_reserved_local_stream->Write(",");
}
} else if (!emboss_reserved_local_field_options.json()) {
emboss_reserved_local_stream->Write(" ");
}
emboss_reserved_local_stream->Write("${field_name}: ");
if (emboss_reserved_local_field_options.json()) {
emboss_reserved_local_stream->Write("\"${field_name}\":");
} else {
emboss_reserved_local_stream->Write("${field_name}: ");
}
${field_name}().WriteToTextStream(emboss_reserved_local_stream,
emboss_reserved_local_field_options);
emboss_reserved_local_wrote_field = true;
if (emboss_reserved_local_field_options.multiline()) {
if (emboss_reserved_local_field_options.multiline() &&
!emboss_reserved_local_field_options.json()) {
emboss_reserved_local_stream->Write("\n");
}
} else if (emboss_reserved_local_field_options.allow_partial_output() &&
Expand Down
128 changes: 125 additions & 3 deletions compiler/back_end/cpp/testcode/text_format_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
// Tests of generated code for text format.
#include <stdint.h>

#include <type_traits>
#include <utility>
#include <vector>
#include <array>
#include <numeric>

#include "gtest/gtest.h"
#include "runtime/cpp/emboss_text_util.h"
#include "testdata/text_format.emb.h"

namespace emboss {
Expand Down Expand Up @@ -93,6 +93,128 @@ TEST(TextFormat, UpdateFromText) {
EXPECT_EQ(view.b().Read(), 4);
}

TEST(TextFormat, JsonOutput) {
::std::array<char, 57> values = {};
::std::iota(values.begin(), values.end(), 0);

const auto view = MakeJsonTestStructView(&values);
EXPECT_EQ(
"{\"one_byte_enum\":\"ZERO\",\"seven_bit_uint\":1,\"one_bit_flag\":"
"false,\"one_byte_uint\":2,\"two_byte_uint\":1027,"
"\"four_byte_uint\":134678021,\"eight_byte_uint\":"
"1157159078456920585,\"uint8_array\":[17,18,19,20,21,22,23,24,"
"25,26],\"uint16_array\":[7195,7709,8223,8737,9251,9765,10279,"
"10793,11307,11821],\"struct_array\":[{\"element_one\":47,"
"\"element_two\":48,\"element_three\":49,\"element_four\":50},"
"{\"element_one\":51,\"element_two\":52,\"element_three\":53,"
"\"element_four\":54}]}",
::emboss::WriteToString(view, TextOutputOptions().Json(true)));
}

TEST(TextFormat, JsonMultilineOutput) {
::std::array<char, 3> values = {1, 2, 3};
const auto view = MakeStructWithSkippedFieldsView(&values);
// In multiline JSON mode, each field is written on its own indented line,
// separated by commas, with the closing brace on its own line.
EXPECT_EQ(
"{\n"
" \"a\":1,\n"
" \"c\":3\n"
"}",
::emboss::WriteToString(
view,
TextOutputOptions().Json(true).Multiline(true).WithIndent(" ")));
}

TEST(TextFormat, JsonOutputRobustness) {
::std::array<char, 57> values = {};
::std::iota(values.begin(), values.end(), 0);

const auto view = MakeJsonTestStructView(&values);
auto options = ::emboss::TextOutputOptions()
.Json(true)
.WithComments(true)
.WithDigitGrouping(true)
.WithNumericBase(16);
EXPECT_EQ(
"{\"one_byte_enum\":\"ZERO\",\"seven_bit_uint\":1,\"one_bit_flag\":"
"false,\"one_byte_uint\":2,\"two_byte_uint\":1027,"
"\"four_byte_uint\":134678021,\"eight_byte_uint\":"
"1157159078456920585,\"uint8_array\":[17,18,19,20,21,22,23,24,"
"25,26],\"uint16_array\":[7195,7709,8223,8737,9251,9765,10279,"
"10793,11307,11821],\"struct_array\":[{\"element_one\":47,"
"\"element_two\":48,\"element_three\":49,\"element_four\":50},"
"{\"element_one\":51,\"element_two\":52,\"element_three\":53,"
"\"element_four\":54}]}",
::emboss::WriteToString(view, options));
}

TEST(TextFormat, DigitGroupingAndNumericBase) {
::std::array<char, 57> values = {};
::std::iota(values.begin(), values.end(), 0);

const auto view = MakeJsonTestStructView(&values);
auto options =
::emboss::TextOutputOptions().WithDigitGrouping(true).WithNumericBase(16);
EXPECT_EQ(
"{ one_byte_enum: ZERO, seven_bit_uint: 0x1, one_bit_flag: false, "
"one_byte_uint: 0x2, two_byte_uint: 0x403, four_byte_uint: 0x807_0605, "
"eight_byte_uint: 0x100f_0e0d_0c0b_0a09, uint8_array: { [0x0]: 0x11, "
"0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, [0x8]: 0x19, 0x1a }, "
"uint16_array: { [0x0]: 0x1c1b, 0x1e1d, 0x201f, 0x2221, 0x2423, 0x2625, "
"0x2827, 0x2a29, [0x8]: 0x2c2b, 0x2e2d }, struct_array: { [0x0]: { "
"element_one: 0x2f, element_two: 0x30, element_three: 0x31, "
"element_four: 0x32 }, { element_one: 0x33, element_two: 0x34, "
"element_three: 0x35, element_four: 0x36 } } }",
::emboss::WriteToString(view, options));
}

TEST(TextFormat, MultilineAndPartial) {
::std::array<char, 1> values = {10};
// MakeVanillaView expects a pointer to an array of size 2, so we have to
// construct the view manually.
auto view = VanillaWriter(values.data(), values.size());
auto options =
::emboss::TextOutputOptions().Multiline(true).WithAllowPartialOutput(
true);
EXPECT_EQ(
"{\n"
"a: 10\n"
"}",
::emboss::WriteToString(view, options));
}

TEST(TextFormat, JsonSkippedFieldOutput) {
::std::array<char, 3> values = {1, 2, 3};
const auto view = MakeStructWithSkippedFieldsView(&values);
EXPECT_EQ("{\"a\":1,\"c\":3}",
::emboss::WriteToString(view, TextOutputOptions().Json(true)));
}

TEST(TextFormat, JsonLargeIntegerAsString) {
::std::array<char, 57> values = {};
::std::iota(values.begin(), values.end(), 0);

const auto view = MakeJsonTestStructView(&values);
auto options = ::emboss::TextOutputOptions()
.Json(true)
.WithJsonLargeIntegerHandling(
JsonLargeIntegerHandling::kLargeAsString);
// With kLargeAsString, the eight_byte_uint should be quoted, but smaller
// integers should remain as numbers.
EXPECT_EQ(
"{\"one_byte_enum\":\"ZERO\",\"seven_bit_uint\":1,\"one_bit_flag\":"
"false,\"one_byte_uint\":2,\"two_byte_uint\":1027,"
"\"four_byte_uint\":134678021,\"eight_byte_uint\":"
"\"1157159078456920585\",\"uint8_array\":[17,18,19,20,21,22,23,24,"
"25,26],\"uint16_array\":[7195,7709,8223,8737,9251,9765,10279,"
"10793,11307,11821],\"struct_array\":[{\"element_one\":47,"
"\"element_two\":48,\"element_three\":49,\"element_four\":50},"
"{\"element_one\":51,\"element_two\":52,\"element_three\":53,"
"\"element_four\":54}]}",
::emboss::WriteToString(view, options));
}

} // namespace
} // namespace test
} // namespace emboss
2 changes: 1 addition & 1 deletion compiler/util/parser_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"""Types related to the LR(1) parser.

This module contains types used by the LR(1) parser, which are also used in
other parts of the compiler:
other parts of the compiler:

SourcePosition: a position (zero-width) within a source file.
SourceLocation: a span within a source file.
Expand Down
Loading
Loading