Merge pull request #567 from biojppm/fix/566_top_anchor

Fix emitter regression: anchors in top-level containers
This commit is contained in:
jpmag
2026-01-07 11:17:08 +00:00
committed by GitHub
5 changed files with 113 additions and 10 deletions

View File

@@ -5,8 +5,9 @@
- Added `ParserOptions::detect_flow_ml()` to enable/disable this behavior - Added `ParserOptions::detect_flow_ml()` to enable/disable this behavior
- Added `EmitOptions::indent_flow_ml()` to control indentation of FLOW_ML containers - Added `EmitOptions::indent_flow_ml()` to control indentation of FLOW_ML containers
- The emit implementation logic was refactored, and is now significantly cleaner - The emit implementation logic was refactored, and is now significantly cleaner
- Emitted YAML will now have anchors emitted before tags, as is customary ([see example](https://play.yaml.io/main/parser?input=LSAhdGFnICZhbmNob3IgfAogIG5vdGUgaG93IHRoZSBhbmNob3IgY29tZXMKICBmaXJzdCBpbiB0aGUgZXZlbnRz)). Previously this - Emitted YAML will now have anchors emitted before tags, as is customary ([see example](https://play.yaml.io/main/parser?input=LSAhdGFnICZhbmNob3IgfAogIG5vdGUgaG93IHRoZSBhbmNob3IgY29tZXMKICBmaXJzdCBpbiB0aGUgZXZlbnRz)).
- Added `ParserOptions` defaulted argument to temp-parser overloads of `parse_{yaml,json}_in_{place,arena}()` - Added `ParserOptions` defaulted argument to temp-parser overloads of `parse_{yaml,json}_in_{place,arena}()`
- [PR#567](https://github.com/biojppm/rapidyaml/pull/567) (fixes [#566](https://github.com/biojppm/rapidyaml/issues/566)) fixes a regression from this refactor where top-level container anchors were wrongly emitted in the same line if no style was set on the container.
### API changes ### API changes

View File

@@ -314,7 +314,7 @@ void Emitter<Writer>::_top_open_entry(id_type node)
else else
{ {
_RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_container(), m_tree, node); _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_container(), m_tree, node);
if(ty.is_block()) if(!ty.is_flow())
_pend_newl(); _pend_newl();
} }
} }

View File

@@ -372,6 +372,105 @@ tpl: &anchor
} }
//-----------------------------------------------------------------------------
Tree github566_make_map(NodeType_e root_style)
{
Tree tree;
NodeRef root = tree.rootref();
root |= MAP|root_style;
root.set_val_anchor("ref0");
root["a"] = "1";
NodeRef self = root["self"];
self.set_val("*ref0");
self.set_val_ref("ref0");
return tree;
}
Tree github566_make_seq(NodeType_e root_style)
{
Tree tree;
NodeRef root = tree.rootref();
root |= SEQ|root_style;
root.set_val_anchor("ref0");
root[0] = "1";
root[1].set_val("*ref0");
root[1].set_val_ref("ref0");
return tree;
}
void github566_cmp(type_bits mask, Tree const& orig, std::string const& expected)
{
const Tree parsed = parse_in_arena(to_csubstr(expected));
test_compare(orig, parsed, "orig", "parsed", mask);
EXPECT_EQ(expected, emitrs_yaml<std::string>(parsed));
EXPECT_EQ(expected, emitrs_yaml<std::string>(orig));
}
TEST(anchors, github566_map_NOSTYLE)
{
const Tree orig = github566_make_map(NOTYPE);
github566_cmp(_TYMASK, orig, R"(&ref0
a: 1
self: *ref0
)");
}
TEST(anchors, github566_map_BLOCK)
{
const Tree orig = github566_make_map(BLOCK);
github566_cmp(0xffffffff, orig, R"(&ref0
a: 1
self: *ref0
)");
}
TEST(anchors, github566_map_FLOW_ML)
{
const Tree orig = github566_make_map(FLOW_ML);
github566_cmp(0xffffffff, orig, R"(&ref0 {
a: 1,
self: *ref0
}
)");
}
TEST(anchors, github566_map_FLOW_SL)
{
const Tree orig = github566_make_map(FLOW_SL);
github566_cmp(0xffffffff, orig, R"(&ref0 {a: 1,self: *ref0})");
}
TEST(anchors, github566_seq_NOSTYLE)
{
const Tree orig = github566_make_seq(NOTYPE);
github566_cmp(_TYMASK, orig, R"(&ref0
- 1
- *ref0
)");
}
TEST(anchors, github566_seq_BLOCK)
{
const Tree orig = github566_make_seq(BLOCK);
github566_cmp(0xffffffff, orig, R"(&ref0
- 1
- *ref0
)");
}
TEST(anchors, github566_seq_FLOW_ML)
{
const Tree orig = github566_make_seq(FLOW_ML);
github566_cmp(0xffffffff, orig, R"(&ref0 [
1,
*ref0
]
)");
}
TEST(anchors, github566_seq_FLOW_SL)
{
const Tree orig = github566_make_seq(FLOW_SL);
github566_cmp(0xffffffff, orig, R"(&ref0 [1,*ref0])");
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@@ -48,20 +48,22 @@ id_type _num_leaves(Tree const& t, id_type node)
void test_compare(Tree const& actual, Tree const& expected, void test_compare(Tree const& actual, Tree const& expected,
const char *actual_name, const char *expected_name) const char *actual_name, const char *expected_name,
type_bits cmp_mask)
{ {
ASSERT_EQ(actual.empty(), expected.empty()); ASSERT_EQ(actual.empty(), expected.empty());
if(actual.empty() || expected.empty()) if(actual.empty() || expected.empty())
return; return;
EXPECT_EQ(actual.size(), expected.size()); EXPECT_EQ(actual.size(), expected.size());
EXPECT_EQ(_num_leaves(actual, actual.root_id()), _num_leaves(expected, expected.root_id())); EXPECT_EQ(_num_leaves(actual, actual.root_id()), _num_leaves(expected, expected.root_id()));
test_compare(actual, actual.root_id(), expected, expected.root_id(), 0, actual_name, expected_name); test_compare(actual, actual.root_id(), expected, expected.root_id(), 0, actual_name, expected_name, cmp_mask);
} }
void test_compare(Tree const& actual, id_type node_actual, void test_compare(Tree const& actual, id_type node_actual,
Tree const& expected, id_type node_expected, Tree const& expected, id_type node_expected,
id_type depth, const char *actual_name, const char *expected_name) id_type depth, const char *actual_name, const char *expected_name,
type_bits cmp_mask)
{ {
RYML_TRACE_FMT("{}[{}] vs {}[{}]. depth={}", actual_name, node_actual, expected_name, node_expected, depth); RYML_TRACE_FMT("{}[{}] vs {}[{}]. depth={}", actual_name, node_actual, expected_name, node_expected, depth);
@@ -70,10 +72,9 @@ void test_compare(Tree const& actual, id_type node_actual,
ASSERT_LT(node_actual, actual.capacity()); ASSERT_LT(node_actual, actual.capacity());
ASSERT_LT(node_expected, expected.capacity()); ASSERT_LT(node_expected, expected.capacity());
NodeType type_actual = actual.type(node_actual)&_TYMASK; NodeType type_actual = actual.type(node_actual) & cmp_mask;
NodeType type_expected = expected.type(node_expected)&_TYMASK; NodeType type_expected = expected.type(node_expected) & cmp_mask;
RYML_COMPARE_NODE_TYPE(type_actual, type_expected, ==, EQ); RYML_COMPARE_NODE_TYPE(type_actual, type_expected, ==, EQ);
//EXPECT_EQ((type_bits)(actual.type(node_actual)&_TYMASK), (type_bits)(expected.type(node_expected)&_TYMASK));
EXPECT_EQ(actual.has_key(node_actual), expected.has_key(node_expected)); EXPECT_EQ(actual.has_key(node_actual), expected.has_key(node_expected));
if(actual.has_key(node_actual) && expected.has_key(node_expected)) if(actual.has_key(node_actual) && expected.has_key(node_expected))

View File

@@ -117,10 +117,12 @@ Case const* get_case(csubstr name);
CaseData* get_data(csubstr name); CaseData* get_data(csubstr name);
void test_compare(Tree const& actual, Tree const& expected, void test_compare(Tree const& actual, Tree const& expected,
const char *actual_name="actual", const char *expected_name="expected"); const char *actual_name="actual", const char *expected_name="expected",
type_bits cmp_mask=_TYMASK);
void test_compare(Tree const& actual, id_type node_actual, void test_compare(Tree const& actual, id_type node_actual,
Tree const& expected, id_type node_expected, Tree const& expected, id_type node_expected,
id_type level=0, const char *actual_name="actual", const char *expected_name="expected"); id_type level=0, const char *actual_name="actual", const char *expected_name="expected",
type_bits cmp_mask=_TYMASK);
void test_arena_not_shared(Tree const& a, Tree const& b); void test_arena_not_shared(Tree const& a, Tree const& b);