Merge 89452973d8 into 39f737443b
This commit is contained in:
commit
3945222014
@ -89,6 +89,8 @@ const char* const INVALID_ANCHOR = "invalid anchor";
|
|||||||
const char* const INVALID_ALIAS = "invalid alias";
|
const char* const INVALID_ALIAS = "invalid alias";
|
||||||
const char* const INVALID_TAG = "invalid tag";
|
const char* const INVALID_TAG = "invalid tag";
|
||||||
const char* const BAD_FILE = "bad file";
|
const char* const BAD_FILE = "bad file";
|
||||||
|
const char* const MERGE_KEY_NEEDS_SINGLE_OR_SEQUENCE_OF_MAPS =
|
||||||
|
"merge key needs either single map or sequence of maps";
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline const std::string KEY_NOT_FOUND_WITH_KEY(
|
inline const std::string KEY_NOT_FOUND_WITH_KEY(
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "nodebuilder.h"
|
#include "nodebuilder.h"
|
||||||
@ -15,6 +16,7 @@ NodeBuilder::NodeBuilder()
|
|||||||
m_stack{},
|
m_stack{},
|
||||||
m_anchors{},
|
m_anchors{},
|
||||||
m_keys{},
|
m_keys{},
|
||||||
|
m_mergeDicts{},
|
||||||
m_mapDepth(0) {
|
m_mapDepth(0) {
|
||||||
m_anchors.push_back(nullptr); // since the anchors start at 1
|
m_anchors.push_back(nullptr); // since the anchors start at 1
|
||||||
}
|
}
|
||||||
@ -69,11 +71,38 @@ void NodeBuilder::OnMapStart(const Mark& mark, const std::string& tag,
|
|||||||
node.set_tag(tag);
|
node.set_tag(tag);
|
||||||
node.set_style(style);
|
node.set_style(style);
|
||||||
m_mapDepth++;
|
m_mapDepth++;
|
||||||
|
m_mergeDicts.emplace_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MergeMapCollection(detail::node& map_to, detail::node& map_from,
|
||||||
|
detail::shared_memory_holder& pMemory) {
|
||||||
|
for (auto j = map_from.begin(); j != map_from.end(); j++) {
|
||||||
|
const auto from_key = j->first;
|
||||||
|
/// NOTE: const_map_to.get(*j->first) cannot be used here, since it
|
||||||
|
/// compares only the shared_ptr's, while we need to compare the key
|
||||||
|
/// itself.
|
||||||
|
///
|
||||||
|
/// NOTE: get() also iterates over elements
|
||||||
|
bool found = std::any_of(map_to.begin(), map_to.end(), [&](const detail::node_iterator_value<detail::node> & kv)
|
||||||
|
{
|
||||||
|
const auto key_node = kv.first;
|
||||||
|
return key_node->scalar() == from_key->scalar();
|
||||||
|
});
|
||||||
|
if (!found)
|
||||||
|
map_to.insert(*from_key, *j->second, pMemory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeBuilder::OnMapEnd() {
|
void NodeBuilder::OnMapEnd() {
|
||||||
assert(m_mapDepth > 0);
|
assert(m_mapDepth > 0);
|
||||||
|
detail::node& collection = *m_stack.back();
|
||||||
|
auto& toMerge = *m_mergeDicts.rbegin();
|
||||||
|
/// The elements for merging should be traversed in reverse order to prefer last values.
|
||||||
|
for (auto it = toMerge.rbegin(); it != toMerge.rend(); ++it) {
|
||||||
|
MergeMapCollection(collection, **it, m_pMemory);
|
||||||
|
}
|
||||||
m_mapDepth--;
|
m_mapDepth--;
|
||||||
|
m_mergeDicts.pop_back();
|
||||||
Pop();
|
Pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,15 +136,40 @@ void NodeBuilder::Pop() {
|
|||||||
m_stack.pop_back();
|
m_stack.pop_back();
|
||||||
|
|
||||||
detail::node& collection = *m_stack.back();
|
detail::node& collection = *m_stack.back();
|
||||||
|
|
||||||
if (collection.type() == NodeType::Sequence) {
|
if (collection.type() == NodeType::Sequence) {
|
||||||
collection.push_back(node, m_pMemory);
|
collection.push_back(node, m_pMemory);
|
||||||
} else if (collection.type() == NodeType::Map) {
|
} else if (collection.type() == NodeType::Map) {
|
||||||
assert(!m_keys.empty());
|
assert(!m_keys.empty());
|
||||||
PushedKey& key = m_keys.back();
|
PushedKey& key = m_keys.back();
|
||||||
if (key.second) {
|
if (key.second) {
|
||||||
collection.insert(*key.first, node, m_pMemory);
|
detail::node& nk = *key.first;
|
||||||
m_keys.pop_back();
|
if (nk.type() == NodeType::Scalar &&
|
||||||
|
((nk.tag() == "tag:yaml.org,2002:merge" && nk.scalar() == "<<") ||
|
||||||
|
(nk.tag() == "?" && nk.scalar() == "<<"))) {
|
||||||
|
if (node.type() == NodeType::Map) {
|
||||||
|
m_mergeDicts.rbegin()->emplace_back(&node);
|
||||||
|
m_keys.pop_back();
|
||||||
|
} else if (node.type() == NodeType::Sequence) {
|
||||||
|
for (auto i = node.begin(); i != node.end(); i++) {
|
||||||
|
auto v = *i;
|
||||||
|
if ((*v).type() == NodeType::Map) {
|
||||||
|
m_mergeDicts.rbegin()->emplace_back(&(*v));
|
||||||
|
} else {
|
||||||
|
throw ParserException(
|
||||||
|
node.mark(),
|
||||||
|
ErrorMsg::MERGE_KEY_NEEDS_SINGLE_OR_SEQUENCE_OF_MAPS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_keys.pop_back();
|
||||||
|
} else {
|
||||||
|
throw ParserException(
|
||||||
|
node.mark(),
|
||||||
|
ErrorMsg::MERGE_KEY_NEEDS_SINGLE_OR_SEQUENCE_OF_MAPS);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
collection.insert(*key.first, node, m_pMemory);
|
||||||
|
m_keys.pop_back();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
key.second = true;
|
key.second = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,6 +67,7 @@ class NodeBuilder : public EventHandler {
|
|||||||
|
|
||||||
using PushedKey = std::pair<detail::node*, bool>;
|
using PushedKey = std::pair<detail::node*, bool>;
|
||||||
std::vector<PushedKey> m_keys;
|
std::vector<PushedKey> m_keys;
|
||||||
|
std::vector<Nodes> m_mergeDicts;
|
||||||
std::size_t m_mapDepth;
|
std::size_t m_mapDepth;
|
||||||
};
|
};
|
||||||
} // namespace YAML
|
} // namespace YAML
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "yaml-cpp/yaml.h" // IWYU pragma: keep
|
#include "yaml-cpp/yaml.h" // IWYU pragma: keep
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace YAML {
|
namespace YAML {
|
||||||
namespace {
|
namespace {
|
||||||
@ -173,6 +174,78 @@ TEST(LoadNodeTest, CloneAlias) {
|
|||||||
EXPECT_EQ(clone[0], clone);
|
EXPECT_EQ(clone[0], clone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(LoadNodeTest, MergeKeyA) {
|
||||||
|
Node node = Load(
|
||||||
|
"{x: &foo {a : 1,b : 1,c : 1}, y: &bar {d: 2, e : 2, f : 2, a : 2}, z: "
|
||||||
|
"&stuff { << : *foo, b : 3} }");
|
||||||
|
EXPECT_EQ(NodeType::Map, node["z"].Type());
|
||||||
|
EXPECT_FALSE(node["z"]["<<"]);
|
||||||
|
EXPECT_EQ(1, node["z"]["a"].as<int>());
|
||||||
|
EXPECT_EQ(3, node["z"]["b"].as<int>());
|
||||||
|
EXPECT_EQ(1, node["z"]["c"].as<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LoadNodeTest, MergeKeyAIterator) {
|
||||||
|
Node node = Load(
|
||||||
|
"{x: &foo {a : 1,b : 1,c : 1}, y: &bar {d: 2, e : 2, f : 2, a : 2}, z: "
|
||||||
|
"&stuff { << : *foo, b : 3} }");
|
||||||
|
EXPECT_EQ(NodeType::Map, node["z"].Type());
|
||||||
|
|
||||||
|
const auto& z = node["z"];
|
||||||
|
size_t z_b_keys = std::count_if(z.begin(), z.end(), [&](const detail::iterator_value & kv)
|
||||||
|
{
|
||||||
|
return kv.first.as<std::string>() == "b";
|
||||||
|
});
|
||||||
|
ASSERT_EQ(z_b_keys, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LoadNodeTest, MergeKeyTwoOverrides) {
|
||||||
|
Node node = Load(R"(
|
||||||
|
trait1: &t1
|
||||||
|
foo: 1
|
||||||
|
|
||||||
|
trait2: &t2
|
||||||
|
foo: 2
|
||||||
|
|
||||||
|
merged:
|
||||||
|
<<: *t1
|
||||||
|
<<: *t2
|
||||||
|
)");
|
||||||
|
EXPECT_EQ(NodeType::Map, node["merged"].Type());
|
||||||
|
EXPECT_FALSE(node["merged"]["<<"]);
|
||||||
|
EXPECT_EQ(2, node["merged"]["foo"].as<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LoadNodeTest, MergeKeyB) {
|
||||||
|
Node node = Load(
|
||||||
|
"{x: &foo {a : 1,b : 1,c : 1}, y: &bar {d: 2, e : 2, f : 2, a : 2}, z: "
|
||||||
|
"&stuff { << : *foo, b : 3}, w: { << : [*stuff, *bar], c: 4 }, v: { '<<' "
|
||||||
|
": *foo } , u : {!!merge << : *bar}, t: {!!merge << : *bar, h: 3} }");
|
||||||
|
EXPECT_EQ(NodeType::Map, node["z"].Type());
|
||||||
|
EXPECT_EQ(NodeType::Map, node["w"].Type());
|
||||||
|
EXPECT_FALSE(node["z"]["<<"]);
|
||||||
|
EXPECT_EQ(1, node["z"]["a"].as<int>());
|
||||||
|
EXPECT_EQ(3, node["z"]["b"].as<int>());
|
||||||
|
EXPECT_EQ(1, node["z"]["c"].as<int>());
|
||||||
|
|
||||||
|
EXPECT_EQ(2, node["w"]["a"].as<int>());
|
||||||
|
EXPECT_EQ(3, node["w"]["b"].as<int>());
|
||||||
|
EXPECT_EQ(4, node["w"]["c"].as<int>());
|
||||||
|
EXPECT_EQ(2, node["w"]["d"].as<int>());
|
||||||
|
EXPECT_EQ(2, node["w"]["e"].as<int>());
|
||||||
|
EXPECT_EQ(2, node["w"]["f"].as<int>());
|
||||||
|
|
||||||
|
EXPECT_TRUE(node["v"]["<<"]);
|
||||||
|
EXPECT_EQ(1, node["v"]["<<"]["a"].as<int>());
|
||||||
|
|
||||||
|
EXPECT_FALSE(node["u"]["<<"]);
|
||||||
|
EXPECT_EQ(2, node["u"]["d"].as<int>());
|
||||||
|
|
||||||
|
EXPECT_FALSE(node["t"]["<<"]);
|
||||||
|
EXPECT_EQ(2, node["t"]["d"].as<int>());
|
||||||
|
EXPECT_EQ(3, node["t"]["h"].as<int>());
|
||||||
|
}
|
||||||
|
|
||||||
TEST(LoadNodeTest, ForceInsertIntoMap) {
|
TEST(LoadNodeTest, ForceInsertIntoMap) {
|
||||||
Node node;
|
Node node;
|
||||||
node["a"] = "b";
|
node["a"] = "b";
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user