diff --git a/src/c4/yml/node.hpp b/src/c4/yml/node.hpp index e27dba262..e1d234171 100644 --- a/src/c4/yml/node.hpp +++ b/src/c4/yml/node.hpp @@ -323,6 +323,14 @@ struct RoNodeMethods C4_ALWAYS_INLINE size_t num_other_siblings() const RYML_NOEXCEPT { _C4RV(); return tree_->num_other_siblings(id_); } C4_ALWAYS_INLINE size_t sibling_pos(ConstImpl const& n) const RYML_NOEXCEPT { _C4RV(); _RYML_CB_ASSERT(tree_->callbacks(), n.readable()); return tree_->child_pos(tree_->parent(id_), n.m_id); } + /** @} */ + +public: + + /** @name square_brackets + * operator[] */ + /** @{ */ + /** Find child by key; complexity is O(num_children). * * Returns the requested node, or an object in seed state if no @@ -332,10 +340,11 @@ struct RoNodeMethods * to the tree provided that its create() method is called prior * to writing, which happens in most modifying methods in * NodeRef. It is the caller's responsibility to verify that the - * returned node is readable before subsequently using it. + * returned node is readable before subsequently using it to read + * from the tree. * * @warning the calling object must be readable. This precondition - * is asserted. This assertion is performed only if @ref + * is asserted. The assertion is performed only if @ref * RYML_USE_ASSERT is set to true. As with the non-const overload, * it is UB to call this method if the node is not readable. * @@ -357,10 +366,11 @@ struct RoNodeMethods * to the tree provided that its create() method is called prior * to writing, which happens in most modifying methods in * NodeRef. It is the caller's responsibility to verify that the - * returned node is readable before subsequently using it. + * returned node is readable before subsequently using it to read + * from the tree. * * @warning the calling object must be readable. This precondition - * is asserted. This assertion is performed only if @ref + * is asserted. The assertion is performed only if @ref * RYML_USE_ASSERT is set to true. As with the non-const overload, * it is UB to call this method if the node is not readable. * @@ -377,7 +387,7 @@ struct RoNodeMethods * * Behaves similar to the non-const overload, but further asserts * that the returned node is readable (because it can never be in - * a seed state). This assertion is performed only if @ref + * a seed state). The assertion is performed only if @ref * RYML_USE_ASSERT is set to true. As with the non-const overload, * it is UB to use the return value if it is not valid. * @@ -407,6 +417,90 @@ struct RoNodeMethods return {tree_, ch}; } + /** @} */ + +public: + + /** @name at + * + * These functions are the analogue to operator[], with the + * difference that they */ + /** @{ */ + + /** Find child by key; complexity is O(num_children). + * + * Returns the requested node, or an object in seed state if no + * such child is found (see @ref NodeRef for an explanation of + * what is seed state). When the object is in seed state, using it + * to read from the tree is UB. The seed node can be subsequently + * used to write to the tree provided that its create() method is + * called prior to writing, which happens inside most mutating + * methods in NodeRef. It is the caller's responsibility to verify + * that the returned node is readable before subsequently using it + * to read from the tree. + * + * @warning This method will call the error callback (regardless + * of build type) whenever any of the following preconditions is + * violated: a) the object is valid (points at a tree and a node), + * b) the calling object must be readable (must not be in seed + * state), c) the calling object must be pointing at a MAP + * node. The preconditions are similar to the non-const + * operator[](csubstr), but instead of using assertions, this + * function directly checks those conditions and calls the error + * callback if any of the checks fail. + * + * @note since it is valid behavior for the returned node to be in + * seed state, the error callback is not invoked when this + * happens. */ + template + C4_ALWAYS_INLINE auto at(csubstr key) -> _C4_IF_MUTABLE(Impl) + { + RYML_CHECK(tree_ != nullptr); + _RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < tree_->capacity())); + _RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable()); + _RYML_CB_CHECK(tree_->m_callbacks, tree_->is_map(id_)); + size_t ch = tree__->find_child(id__, key); + return ch != NONE ? Impl(tree__, ch) : Impl(tree__, id__, key); + } + + /** Find child by position; complexity is O(pos). + * + * Returns the requested node, or an object in seed state if no + * such child is found (see @ref NodeRef for an explanation of + * what is seed state). When the object is in seed state, using it + * to read from the tree is UB. The seed node can be used to write + * to the tree provided that its create() method is called prior + * to writing, which happens in most modifying methods in + * NodeRef. It is the caller's responsibility to verify that the + * returned node is readable before subsequently using it to read + * from the tree. + * + * @warning This method will call the error callback (regardless + * of build type) whenever any of the following preconditions is + * violated: a) the object is valid (points at a tree and a node), + * b) the calling object must be readable (must not be in seed + * state), c) the calling object must be pointing at a MAP + * node. The preconditions are similar to the non-const + * operator[](size_t), but instead of using assertions, this + * function directly checks those conditions and calls the error + * callback if any of the checks fail. + * + * @note since it is valid behavior for the returned node to be in + * seed state, the error callback is not invoked when this + * happens. */ + template + C4_ALWAYS_INLINE auto at(size_t pos) -> _C4_IF_MUTABLE(Impl) + { + RYML_CHECK(tree_ != nullptr); + const size_t cap = tree_->capacity(); + _RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < cap)); + _RYML_CB_CHECK(tree_->m_callbacks, (pos >= 0 && pos < cap)); + _RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable()); + _RYML_CB_CHECK(tree_->m_callbacks, tree_->is_container(id_)); + size_t ch = tree__->child(id__, pos); + return ch != NONE ? Impl(tree__, ch) : Impl(tree__, id__, pos); + } + /** Get a child by name, with error checking; complexity is * O(num_children). * @@ -419,8 +513,9 @@ struct RoNodeMethods ConstImpl at(csubstr key) const { RYML_CHECK(tree_ != nullptr); + _RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < tree_->capacity())); _RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable()); - _RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->is_map()); + _RYML_CB_CHECK(tree_->m_callbacks, tree_->is_map(id_)); size_t ch = tree_->find_child(id_, key); _RYML_CB_CHECK(tree_->m_callbacks, ch != NONE); return {tree_, ch}; @@ -438,10 +533,13 @@ struct RoNodeMethods ConstImpl at(size_t pos) const { RYML_CHECK(tree_ != nullptr); + const size_t cap = tree_->capacity(); + _RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < cap)); + _RYML_CB_CHECK(tree_->m_callbacks, (pos >= 0 && pos < cap)); _RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable()); - _RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->is_container()); + _RYML_CB_CHECK(tree_->m_callbacks, tree_->is_container(id_)); size_t ch = tree_->child(id_, pos); - _RYML_CB_ASSERT(tree_->m_callbacks, ch != NONE); + _RYML_CB_CHECK(tree_->m_callbacks, ch != NONE); return {tree_, ch}; } @@ -650,6 +748,13 @@ struct RoNodeMethods //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- +/** This object holds a pointer to an existing tree, and a node id. It + * can be used only to read from the tree. + * + * + * + * @warning The lifetime of the tree must be larger than that of this + * object. It is up to the user to ensure that this happens. */ class RYML_EXPORT ConstNodeRef : public detail::RoNodeMethods { public: @@ -746,9 +851,21 @@ class RYML_EXPORT ConstNodeRef : public detail::RoNodeMethods { public: @@ -1318,6 +1435,7 @@ class RYML_EXPORT NodeRef : public detail::RoNodeMethods /** @} */ #undef _C4RV +#undef _C4RID }; diff --git a/test/test_noderef.cpp b/test/test_noderef.cpp index d41082758..a54b906f9 100644 --- a/test/test_noderef.cpp +++ b/test/test_noderef.cpp @@ -181,133 +181,132 @@ TEST(NodeRef, valid_vs_seed_vs_readable) EXPECT_FALSE(none.readable()); } -#define _TEST_FAIL_READ(method_expr) \ +#define _TEST_FAIL(method_expr) \ { \ SCOPED_TRACE(#method_expr); \ - std::cout << __FILE__ << ":" << __LINE__ << ": " << #method_expr << "\n"; \ if(tree) \ ExpectError::check_assertion(tree, [&]{ return method_expr; }); \ else \ ExpectError::check_assertion([&]{ return method_expr; }); \ } -#define _TEST_SUCCEED_READ(method_expr) \ +#define _TEST_SUCCEED(method_expr) \ { \ SCOPED_TRACE(#method_expr); \ - std::cout << __FILE__ << ":" << __LINE__ << ": " << #method_expr << "\n"; \ if(tree) \ ExpectError::check_success(tree, [&]{ return method_expr; }); \ else \ ExpectError::check_success([&]{ return method_expr; }); \ } + template void test_fail_read(Tree *tree, NodeT node) { - _TEST_SUCCEED_READ(node.get()) - _TEST_FAIL_READ(node.type()) - _TEST_FAIL_READ(node.type_str()) - _TEST_FAIL_READ(node.key()) - _TEST_FAIL_READ(node.key_tag()) - _TEST_FAIL_READ(node.key_anchor()) - _TEST_FAIL_READ(node.key_ref()) - _TEST_FAIL_READ(node.key_is_null()) - _TEST_FAIL_READ(node.keysc()) - _TEST_FAIL_READ(node.val()) - _TEST_FAIL_READ(node.val_tag()) - _TEST_FAIL_READ(node.val_anchor()) - _TEST_FAIL_READ(node.val_ref()) - _TEST_FAIL_READ(node.val_is_null()) - _TEST_FAIL_READ(node.valsc()) - _TEST_FAIL_READ(node.is_map()) - _TEST_FAIL_READ(node.empty()) - _TEST_FAIL_READ(node.is_stream()) - _TEST_FAIL_READ(node.is_doc()) - _TEST_FAIL_READ(node.is_container()) - _TEST_FAIL_READ(node.is_map()) - _TEST_FAIL_READ(node.is_seq()) - _TEST_FAIL_READ(node.has_val()) - _TEST_FAIL_READ(node.has_key()) - _TEST_FAIL_READ(node.is_keyval()) - _TEST_FAIL_READ(node.has_key_tag()) - _TEST_FAIL_READ(node.has_val_tag()) - _TEST_FAIL_READ(node.has_key_anchor()) - _TEST_FAIL_READ(node.has_val_anchor()) - _TEST_FAIL_READ(node.is_val_anchor()) - _TEST_FAIL_READ(node.has_anchor()) - _TEST_FAIL_READ(node.is_anchor()) - _TEST_FAIL_READ(node.is_key_ref()) - _TEST_FAIL_READ(node.is_val_ref()) - _TEST_FAIL_READ(node.is_ref()) - _TEST_FAIL_READ(node.is_anchor_or_ref()) - _TEST_FAIL_READ(node.is_key_quoted()) - _TEST_FAIL_READ(node.is_val_quoted()) - _TEST_FAIL_READ(node.parent_is_seq()) - _TEST_FAIL_READ(node.parent_is_map()) - _TEST_FAIL_READ(node.is_root()) - _TEST_FAIL_READ(node.has_parent()) - _TEST_FAIL_READ(node.has_child(0)) - _TEST_FAIL_READ(node.has_child("key")) - _TEST_FAIL_READ(node.has_children()) - _TEST_FAIL_READ(node.has_sibling("key")) - _TEST_FAIL_READ(node.has_other_siblings()) - _TEST_FAIL_READ(node.doc(0)) - _TEST_FAIL_READ(node.parent()) - _TEST_FAIL_READ(node.num_children()) - _TEST_FAIL_READ(node.first_child()) - _TEST_FAIL_READ(node.last_child()) - _TEST_FAIL_READ(node.child(0)) - _TEST_FAIL_READ(node.find_child("key")) - _TEST_FAIL_READ(node.prev_sibling()) - _TEST_FAIL_READ(node.next_sibling()) - _TEST_FAIL_READ(node.first_sibling()) - _TEST_FAIL_READ(node.last_sibling()) - _TEST_FAIL_READ(node.sibling(0)) - _TEST_FAIL_READ(node.find_sibling("key")) - _TEST_FAIL_READ(node.num_children()) - _TEST_FAIL_READ(node.num_siblings()) - _TEST_FAIL_READ(node.num_other_siblings()) - _TEST_FAIL_READ(node["key"]) - _TEST_FAIL_READ(node[0]) - _TEST_FAIL_READ(node.at("key")) - _TEST_FAIL_READ(node.at(0)) + _TEST_SUCCEED(node.get()) + _TEST_FAIL(node.type()) + _TEST_FAIL(node.type_str()) + _TEST_FAIL(node.key()) + _TEST_FAIL(node.key_tag()) + _TEST_FAIL(node.key_anchor()) + _TEST_FAIL(node.key_ref()) + _TEST_FAIL(node.key_is_null()) + _TEST_FAIL(node.keysc()) + _TEST_FAIL(node.val()) + _TEST_FAIL(node.val_tag()) + _TEST_FAIL(node.val_anchor()) + _TEST_FAIL(node.val_ref()) + _TEST_FAIL(node.val_is_null()) + _TEST_FAIL(node.valsc()) + _TEST_FAIL(node.is_map()) + _TEST_FAIL(node.empty()) + _TEST_FAIL(node.is_stream()) + _TEST_FAIL(node.is_doc()) + _TEST_FAIL(node.is_container()) + _TEST_FAIL(node.is_map()) + _TEST_FAIL(node.is_seq()) + _TEST_FAIL(node.has_val()) + _TEST_FAIL(node.has_key()) + _TEST_FAIL(node.is_keyval()) + _TEST_FAIL(node.has_key_tag()) + _TEST_FAIL(node.has_val_tag()) + _TEST_FAIL(node.has_key_anchor()) + _TEST_FAIL(node.has_val_anchor()) + _TEST_FAIL(node.is_val_anchor()) + _TEST_FAIL(node.has_anchor()) + _TEST_FAIL(node.is_anchor()) + _TEST_FAIL(node.is_key_ref()) + _TEST_FAIL(node.is_val_ref()) + _TEST_FAIL(node.is_ref()) + _TEST_FAIL(node.is_anchor_or_ref()) + _TEST_FAIL(node.is_key_quoted()) + _TEST_FAIL(node.is_val_quoted()) + _TEST_FAIL(node.parent_is_seq()) + _TEST_FAIL(node.parent_is_map()) + _TEST_FAIL(node.is_root()) + _TEST_FAIL(node.has_parent()) + _TEST_FAIL(node.has_child(0)) + _TEST_FAIL(node.has_child("key")) + _TEST_FAIL(node.has_children()) + _TEST_FAIL(node.has_sibling("key")) + _TEST_FAIL(node.has_other_siblings()) + _TEST_FAIL(node.doc(0)) + _TEST_FAIL(node.parent()) + _TEST_FAIL(node.num_children()) + _TEST_FAIL(node.first_child()) + _TEST_FAIL(node.last_child()) + _TEST_FAIL(node.child(0)) + _TEST_FAIL(node.find_child("key")) + _TEST_FAIL(node.prev_sibling()) + _TEST_FAIL(node.next_sibling()) + _TEST_FAIL(node.first_sibling()) + _TEST_FAIL(node.last_sibling()) + _TEST_FAIL(node.sibling(0)) + _TEST_FAIL(node.find_sibling("key")) + _TEST_FAIL(node.num_children()) + _TEST_FAIL(node.num_siblings()) + _TEST_FAIL(node.num_other_siblings()) + _TEST_FAIL(node["key"]) + _TEST_FAIL(node[0]) + _TEST_FAIL(node.at("key")) + _TEST_FAIL(node.at(0)) int val; - _TEST_FAIL_READ(node >> val) - _TEST_FAIL_READ(node >> key(val)) - _TEST_FAIL_READ(node >> fmt::base64(val)) - _TEST_FAIL_READ(node >> key(fmt::base64(val))) - _TEST_FAIL_READ(node.deserialize_key(fmt::base64(val))) - _TEST_FAIL_READ(node.deserialize_val(fmt::base64(val))) - _TEST_FAIL_READ(node.get_if("key", &val)); - _TEST_FAIL_READ(node.get_if("key", &val, 0)); + _TEST_FAIL(node >> val) + _TEST_FAIL(node >> key(val)) + _TEST_FAIL(node >> fmt::base64(val)) + _TEST_FAIL(node >> key(fmt::base64(val))) + _TEST_FAIL(node.deserialize_key(fmt::base64(val))) + _TEST_FAIL(node.deserialize_val(fmt::base64(val))) + _TEST_FAIL(node.get_if("key", &val)); + _TEST_FAIL(node.get_if("key", &val, 0)); const NodeT const_node = node; - _TEST_FAIL_READ(node.begin()); - _TEST_FAIL_READ(node.cbegin()); - _TEST_FAIL_READ(const_node.begin()); - _TEST_FAIL_READ(const_node.cbegin()); - _TEST_FAIL_READ(node.end()); - _TEST_FAIL_READ(node.end()); - _TEST_FAIL_READ(const_node.end()); - _TEST_FAIL_READ(const_node.end()); - _TEST_FAIL_READ(node.children()); - _TEST_FAIL_READ(node.children()); - _TEST_FAIL_READ(const_node.children()); - _TEST_FAIL_READ(const_node.children()); - _TEST_FAIL_READ(node.siblings()); - _TEST_FAIL_READ(node.siblings()); - _TEST_FAIL_READ(const_node.siblings()); - _TEST_FAIL_READ(const_node.siblings()); - //_TEST_FAIL_READ(node.visit([](NodeT &n, size_t level){ (void)n; (void)level; return false; })); - //_TEST_FAIL_READ(const_node.visit([](const NodeT &n, size_t level){ (void)n; (void)level; return false; })); - _TEST_SUCCEED_READ(const_node == node); - _TEST_SUCCEED_READ(const_node != node); - _TEST_SUCCEED_READ(const_node == nullptr); - _TEST_SUCCEED_READ(const_node != nullptr); - _TEST_FAIL_READ(const_node == "val"); - _TEST_FAIL_READ(const_node != "val"); + _TEST_FAIL(node.begin()); + _TEST_FAIL(node.cbegin()); + _TEST_FAIL(const_node.begin()); + _TEST_FAIL(const_node.cbegin()); + _TEST_FAIL(node.end()); + _TEST_FAIL(node.end()); + _TEST_FAIL(const_node.end()); + _TEST_FAIL(const_node.end()); + _TEST_FAIL(node.children()); + _TEST_FAIL(node.children()); + _TEST_FAIL(const_node.children()); + _TEST_FAIL(const_node.children()); + _TEST_FAIL(node.siblings()); + _TEST_FAIL(node.siblings()); + _TEST_FAIL(const_node.siblings()); + _TEST_FAIL(const_node.siblings()); + //_TEST_FAIL(node.visit([](NodeT &n, size_t level){ (void)n; (void)level; return false; })); + //_TEST_FAIL(const_node.visit([](const NodeT &n, size_t level){ (void)n; (void)level; return false; })); + _TEST_SUCCEED(const_node == node); + _TEST_SUCCEED(const_node != node); + _TEST_SUCCEED(const_node == nullptr); + _TEST_SUCCEED(const_node != nullptr); + _TEST_FAIL(const_node == "val"); + _TEST_FAIL(const_node != "val"); if(std::is_same::value) { ConstNodeRef other; - _TEST_SUCCEED_READ(node == other); - _TEST_SUCCEED_READ(node != node); + _TEST_SUCCEED(node == other); + _TEST_SUCCEED(node != node); } } template @@ -315,16 +314,16 @@ void test_fail_read_subject(Tree *tree, NodeT node, NodeT subject) { if(node.readable()) { - _TEST_SUCCEED_READ(node.has_child(subject)) - _TEST_SUCCEED_READ(node.has_sibling(subject)) + _TEST_SUCCEED(node.has_child(subject)) + _TEST_SUCCEED(node.has_sibling(subject)) } else { - _TEST_FAIL_READ(node.has_child(subject)) - _TEST_FAIL_READ(node.has_sibling(subject)) + _TEST_FAIL(node.has_child(subject)) + _TEST_FAIL(node.has_sibling(subject)) } - _TEST_FAIL_READ(node.child_pos(subject)) - _TEST_FAIL_READ(node.sibling_pos(subject)) + _TEST_FAIL(node.child_pos(subject)) + _TEST_FAIL(node.sibling_pos(subject)) } #undef _TEST_FAIL_READ #undef _TEST_SUCCEED_READ diff --git a/test/test_tree.cpp b/test/test_tree.cpp index 8689c874e..04fce801a 100644 --- a/test/test_tree.cpp +++ b/test/test_tree.cpp @@ -833,14 +833,30 @@ TEST(Tree, clear) //------------------------------------------- template -void verify_assertion(Tree &tree, Function &&fn) +void verify_success_(Tree &tree, Function &&fn) +{ + ExpectError::check_success(&tree, [&]{ + (void)fn(tree); + }); +} +template +void verify_success_(csubstr src, Function &&fn) +{ + Tree tree = parse_in_arena(src); + ExpectError::check_success(&tree, [&]{ + (void)fn(tree); + }); +} + +template +void verify_assertion_(Tree &tree, Function &&fn) { ExpectError::check_assertion(&tree, [&]{ (void)fn(tree); }); } template -void verify_assertion(csubstr src, Function &&fn) +void verify_assertion_(csubstr src, Function &&fn) { Tree tree = parse_in_arena(src); ExpectError::check_assertion(&tree, [&]{ @@ -849,14 +865,14 @@ void verify_assertion(csubstr src, Function &&fn) } template -void verify_error(Tree &tree, Function &&fn) +void verify_error_(Tree &tree, Function &&fn) { ExpectError::do_check(&tree, [&]{ (void)fn(tree); }); } template -void verify_error(csubstr src, Function &&fn) +void verify_error_(csubstr src, Function &&fn) { Tree tree = parse_in_arena(src); ExpectError::do_check(&tree, [&]{ @@ -864,6 +880,24 @@ void verify_error(csubstr src, Function &&fn) }); } +#define verify_success(...) \ + { \ + SCOPED_TRACE("caller"); \ + verify_success_(__VA_ARGS__); \ + } + +#define verify_assertion(...) \ + { \ + SCOPED_TRACE("caller"); \ + verify_assertion_(__VA_ARGS__); \ + } + +#define verify_error(...) \ + { \ + SCOPED_TRACE("caller"); \ + verify_error_(__VA_ARGS__); \ + } + TEST(Tree, ref) { @@ -917,178 +951,221 @@ TEST(Tree, ref_const) } -TEST(Tree, operator_square_brackets) +TEST(Tree, operator_square_brackets_seq) { - { - Tree t = parse_in_arena("[0, 1, 2, 3, 4]"); - Tree &m = t; - Tree const& cm = t; - EXPECT_EQ(m[0].val(), "0"); - EXPECT_EQ(m[1].val(), "1"); - EXPECT_EQ(m[2].val(), "2"); - EXPECT_EQ(m[3].val(), "3"); - EXPECT_EQ(m[4].val(), "4"); - EXPECT_EQ(cm[0].val(), "0"); - EXPECT_EQ(cm[1].val(), "1"); - EXPECT_EQ(cm[2].val(), "2"); - EXPECT_EQ(cm[3].val(), "3"); - EXPECT_EQ(cm[4].val(), "4"); - // - EXPECT_TRUE(m[0] == "0"); - EXPECT_TRUE(m[1] == "1"); - EXPECT_TRUE(m[2] == "2"); - EXPECT_TRUE(m[3] == "3"); - EXPECT_TRUE(m[4] == "4"); - EXPECT_TRUE(cm[0] == "0"); - EXPECT_TRUE(cm[1] == "1"); - EXPECT_TRUE(cm[2] == "2"); - EXPECT_TRUE(cm[3] == "3"); - EXPECT_TRUE(cm[4] == "4"); - // - EXPECT_FALSE(m[0] != "0"); - EXPECT_FALSE(m[1] != "1"); - EXPECT_FALSE(m[2] != "2"); - EXPECT_FALSE(m[3] != "3"); - EXPECT_FALSE(m[4] != "4"); - EXPECT_FALSE(cm[0] != "0"); - EXPECT_FALSE(cm[1] != "1"); - EXPECT_FALSE(cm[2] != "2"); - EXPECT_FALSE(cm[3] != "3"); - EXPECT_FALSE(cm[4] != "4"); - // - verify_assertion(t, [&](Tree const&){ return cm[m.capacity()]; }); - verify_assertion(t, [&](Tree const&){ return cm[NONE]; }); - verify_assertion(t, [&](Tree const&){ return cm[0][0]; }); - verify_assertion(t, [&](Tree const&){ return cm["a"]; }); - } - { - Tree t = parse_in_arena("{a: 0, b: 1, c: 2, d: 3, e: 4}"); - Tree &m = t; - Tree const& cm = t; - EXPECT_EQ(m["a"].val(), "0"); - EXPECT_EQ(m["b"].val(), "1"); - EXPECT_EQ(m["c"].val(), "2"); - EXPECT_EQ(m["d"].val(), "3"); - EXPECT_EQ(m["e"].val(), "4"); - EXPECT_EQ(cm["a"].val(), "0"); - EXPECT_EQ(cm["b"].val(), "1"); - EXPECT_EQ(cm["c"].val(), "2"); - EXPECT_EQ(cm["d"].val(), "3"); - EXPECT_EQ(cm["e"].val(), "4"); - // - EXPECT_TRUE(m["a"] == "0"); - EXPECT_TRUE(m["b"] == "1"); - EXPECT_TRUE(m["c"] == "2"); - EXPECT_TRUE(m["d"] == "3"); - EXPECT_TRUE(m["e"] == "4"); - EXPECT_TRUE(cm["a"] == "0"); - EXPECT_TRUE(cm["b"] == "1"); - EXPECT_TRUE(cm["c"] == "2"); - EXPECT_TRUE(cm["d"] == "3"); - EXPECT_TRUE(cm["e"] == "4"); - // - EXPECT_FALSE(m["a"] != "0"); - EXPECT_FALSE(m["b"] != "1"); - EXPECT_FALSE(m["c"] != "2"); - EXPECT_FALSE(m["d"] != "3"); - EXPECT_FALSE(m["e"] != "4"); - EXPECT_FALSE(cm["a"] != "0"); - EXPECT_FALSE(cm["b"] != "1"); - EXPECT_FALSE(cm["c"] != "2"); - EXPECT_FALSE(cm["d"] != "3"); - EXPECT_FALSE(cm["e"] != "4"); - // - verify_assertion(t, [&](Tree const&){ return cm["f"]; }); - verify_assertion(t, [&](Tree const&){ return cm["g"]["h"]; }); - } + Tree t = parse_in_arena("[0, 1, 2, 3, 4]"); + Tree &m = t; + Tree const& cm = t; + EXPECT_EQ(m[0].val(), "0"); + EXPECT_EQ(m[1].val(), "1"); + EXPECT_EQ(m[2].val(), "2"); + EXPECT_EQ(m[3].val(), "3"); + EXPECT_EQ(m[4].val(), "4"); + EXPECT_EQ(cm[0].val(), "0"); + EXPECT_EQ(cm[1].val(), "1"); + EXPECT_EQ(cm[2].val(), "2"); + EXPECT_EQ(cm[3].val(), "3"); + EXPECT_EQ(cm[4].val(), "4"); + // + EXPECT_TRUE(m[0] == "0"); + EXPECT_TRUE(m[1] == "1"); + EXPECT_TRUE(m[2] == "2"); + EXPECT_TRUE(m[3] == "3"); + EXPECT_TRUE(m[4] == "4"); + EXPECT_TRUE(cm[0] == "0"); + EXPECT_TRUE(cm[1] == "1"); + EXPECT_TRUE(cm[2] == "2"); + EXPECT_TRUE(cm[3] == "3"); + EXPECT_TRUE(cm[4] == "4"); + // + EXPECT_FALSE(m[0] != "0"); + EXPECT_FALSE(m[1] != "1"); + EXPECT_FALSE(m[2] != "2"); + EXPECT_FALSE(m[3] != "3"); + EXPECT_FALSE(m[4] != "4"); + EXPECT_FALSE(cm[0] != "0"); + EXPECT_FALSE(cm[1] != "1"); + EXPECT_FALSE(cm[2] != "2"); + EXPECT_FALSE(cm[3] != "3"); + EXPECT_FALSE(cm[4] != "4"); + // + verify_assertion(t, [&](Tree const&){ return cm[m.capacity()]; }); + verify_assertion(t, [&](Tree const&){ return cm[NONE]; }); + verify_assertion(t, [&](Tree const&){ return cm[0][0]; }); + verify_assertion(t, [&](Tree const&){ return cm["a"]; }); } -TEST(Tree, noderef_at) +TEST(Tree, operator_square_brackets_map) { - { - Tree t = parse_in_arena("[0, 1, 2, 3, 4]"); - NodeRef m = t.rootref(); - ConstNodeRef const cm = t.rootref(); - EXPECT_EQ(m.at(0).val(), "0"); - EXPECT_EQ(m.at(1).val(), "1"); - EXPECT_EQ(m.at(2).val(), "2"); - EXPECT_EQ(m.at(3).val(), "3"); - EXPECT_EQ(m.at(4).val(), "4"); - EXPECT_EQ(cm.at(0).val(), "0"); - EXPECT_EQ(cm.at(1).val(), "1"); - EXPECT_EQ(cm.at(2).val(), "2"); - EXPECT_EQ(cm.at(3).val(), "3"); - EXPECT_EQ(cm.at(4).val(), "4"); - // - EXPECT_TRUE(m.at(0) == "0"); - EXPECT_TRUE(m.at(1) == "1"); - EXPECT_TRUE(m.at(2) == "2"); - EXPECT_TRUE(m.at(3) == "3"); - EXPECT_TRUE(m.at(4) == "4"); - EXPECT_TRUE(cm.at(0) == "0"); - EXPECT_TRUE(cm.at(1) == "1"); - EXPECT_TRUE(cm.at(2) == "2"); - EXPECT_TRUE(cm.at(3) == "3"); - EXPECT_TRUE(cm.at(4) == "4"); - // - EXPECT_FALSE(m.at(0) != "0"); - EXPECT_FALSE(m.at(1) != "1"); - EXPECT_FALSE(m.at(2) != "2"); - EXPECT_FALSE(m.at(3) != "3"); - EXPECT_FALSE(m.at(4) != "4"); - EXPECT_FALSE(cm.at(0) != "0"); - EXPECT_FALSE(cm.at(1) != "1"); - EXPECT_FALSE(cm.at(2) != "2"); - EXPECT_FALSE(cm.at(3) != "3"); - EXPECT_FALSE(cm.at(4) != "4"); - // - verify_error(t, [&](Tree const&){ return cm[t.capacity()]; }); - verify_error(t, [&](Tree const&){ return cm.at(NONE); }); - verify_error(t, [&](Tree const&){ return cm.at(0).at(0); }); - verify_error(t, [&](Tree const&){ return cm.at("a"); }); - } - { - Tree t = parse_in_arena("{a: 0, b: 1, c: 2, d: 3, e: 4}"); - NodeRef m = t.rootref(); - ConstNodeRef const cm = t.rootref(); - EXPECT_EQ(m.at("a").val(), "0"); - EXPECT_EQ(m.at("b").val(), "1"); - EXPECT_EQ(m.at("c").val(), "2"); - EXPECT_EQ(m.at("d").val(), "3"); - EXPECT_EQ(m.at("e").val(), "4"); - EXPECT_EQ(cm.at("a").val(), "0"); - EXPECT_EQ(cm.at("b").val(), "1"); - EXPECT_EQ(cm.at("c").val(), "2"); - EXPECT_EQ(cm.at("d").val(), "3"); - EXPECT_EQ(cm.at("e").val(), "4"); - // - EXPECT_TRUE(m.at("a") == "0"); - EXPECT_TRUE(m.at("b") == "1"); - EXPECT_TRUE(m.at("c") == "2"); - EXPECT_TRUE(m.at("d") == "3"); - EXPECT_TRUE(m.at("e") == "4"); - EXPECT_TRUE(cm.at("a") == "0"); - EXPECT_TRUE(cm.at("b") == "1"); - EXPECT_TRUE(cm.at("c") == "2"); - EXPECT_TRUE(cm.at("d") == "3"); - EXPECT_TRUE(cm.at("e") == "4"); - // - EXPECT_FALSE(m.at("a") != "0"); - EXPECT_FALSE(m.at("b") != "1"); - EXPECT_FALSE(m.at("c") != "2"); - EXPECT_FALSE(m.at("d") != "3"); - EXPECT_FALSE(m.at("e") != "4"); - EXPECT_FALSE(cm.at("a") != "0"); - EXPECT_FALSE(cm.at("b") != "1"); - EXPECT_FALSE(cm.at("c") != "2"); - EXPECT_FALSE(cm.at("d") != "3"); - EXPECT_FALSE(cm.at("e") != "4"); - // - verify_error(t, [&](Tree const&){ return cm[t.capacity()]; }); - verify_error(t, [&](Tree const&){ return cm.at(NONE); }); - verify_error(t, [&](Tree const&){ return cm.at("f"); }); - verify_error(t, [&](Tree const&){ return cm.at("g").at("h"); }); - } + Tree t = parse_in_arena("{a: 0, b: 1, c: 2, d: 3, e: 4}"); + Tree &m = t; + Tree const& cm = t; + EXPECT_EQ(m["a"].val(), "0"); + EXPECT_EQ(m["b"].val(), "1"); + EXPECT_EQ(m["c"].val(), "2"); + EXPECT_EQ(m["d"].val(), "3"); + EXPECT_EQ(m["e"].val(), "4"); + EXPECT_EQ(cm["a"].val(), "0"); + EXPECT_EQ(cm["b"].val(), "1"); + EXPECT_EQ(cm["c"].val(), "2"); + EXPECT_EQ(cm["d"].val(), "3"); + EXPECT_EQ(cm["e"].val(), "4"); + // + EXPECT_TRUE(m["a"] == "0"); + EXPECT_TRUE(m["b"] == "1"); + EXPECT_TRUE(m["c"] == "2"); + EXPECT_TRUE(m["d"] == "3"); + EXPECT_TRUE(m["e"] == "4"); + EXPECT_TRUE(cm["a"] == "0"); + EXPECT_TRUE(cm["b"] == "1"); + EXPECT_TRUE(cm["c"] == "2"); + EXPECT_TRUE(cm["d"] == "3"); + EXPECT_TRUE(cm["e"] == "4"); + // + EXPECT_FALSE(m["a"] != "0"); + EXPECT_FALSE(m["b"] != "1"); + EXPECT_FALSE(m["c"] != "2"); + EXPECT_FALSE(m["d"] != "3"); + EXPECT_FALSE(m["e"] != "4"); + EXPECT_FALSE(cm["a"] != "0"); + EXPECT_FALSE(cm["b"] != "1"); + EXPECT_FALSE(cm["c"] != "2"); + EXPECT_FALSE(cm["d"] != "3"); + EXPECT_FALSE(cm["e"] != "4"); + // + verify_assertion(t, [&](Tree const&){ return cm["f"]; }); + verify_assertion(t, [&](Tree const&){ return cm["g"]["h"]; }); +} + +TEST(Tree, noderef_at_seq) +{ + Tree t = parse_in_arena("[0, 1, 2, 3, 4]"); + NodeRef m = t.rootref(); + ConstNodeRef const cm = t.rootref(); + EXPECT_EQ(m.at(0).val(), "0"); + EXPECT_EQ(m.at(1).val(), "1"); + EXPECT_EQ(m.at(2).val(), "2"); + EXPECT_EQ(m.at(3).val(), "3"); + EXPECT_EQ(m.at(4).val(), "4"); + EXPECT_EQ(cm.at(0).val(), "0"); + EXPECT_EQ(cm.at(1).val(), "1"); + EXPECT_EQ(cm.at(2).val(), "2"); + EXPECT_EQ(cm.at(3).val(), "3"); + EXPECT_EQ(cm.at(4).val(), "4"); + // + EXPECT_TRUE(m.at(0) == "0"); + EXPECT_TRUE(m.at(1) == "1"); + EXPECT_TRUE(m.at(2) == "2"); + EXPECT_TRUE(m.at(3) == "3"); + EXPECT_TRUE(m.at(4) == "4"); + EXPECT_TRUE(cm.at(0) == "0"); + EXPECT_TRUE(cm.at(1) == "1"); + EXPECT_TRUE(cm.at(2) == "2"); + EXPECT_TRUE(cm.at(3) == "3"); + EXPECT_TRUE(cm.at(4) == "4"); + // + EXPECT_FALSE(m.at(0) != "0"); + EXPECT_FALSE(m.at(1) != "1"); + EXPECT_FALSE(m.at(2) != "2"); + EXPECT_FALSE(m.at(3) != "3"); + EXPECT_FALSE(m.at(4) != "4"); + EXPECT_FALSE(cm.at(0) != "0"); + EXPECT_FALSE(cm.at(1) != "1"); + EXPECT_FALSE(cm.at(2) != "2"); + EXPECT_FALSE(cm.at(3) != "3"); + EXPECT_FALSE(cm.at(4) != "4"); + // + EXPECT_EQ(cm.num_children(), 5); + EXPECT_EQ(cm.num_children(), m.num_children()); + // + verify_error(t, [&](Tree const&){ return cm.at(NONE); }); + verify_error(t, [&](Tree const&){ return cm.at(t.capacity()); }); + verify_error(t, [&](Tree const&){ return cm.at(5); }); + verify_error(t, [&](Tree const&){ return cm.at(6); }); + verify_error(t, [&](Tree const&){ return cm.at(7); }); + verify_error(t, [&](Tree const&){ return cm.at(10); }); + verify_error(t, [&](Tree const&){ return cm.at(0).at(0); }); + verify_error(t, [&](Tree const&){ return cm.at("a"); }); + // + verify_error(t, [&](Tree const&){ return m.at(NONE); }); + verify_error(t, [&](Tree const&){ return m.at(t.capacity()); }); + verify_success(t, [&](Tree const&){ return m.at(5); }); + verify_success(t, [&](Tree const&){ return m.at(6); }); + verify_error(t, [&](Tree const&){ return m.at(0).at(0); }); + verify_error(t, [&](Tree const&){ return m.at("a"); }); + EXPECT_TRUE(m.at(5).is_seed()); + EXPECT_TRUE(m.at(6).is_seed()); + // + NodeRef to_be_removed_orig = m.at(4); + NodeRef to_be_removed = to_be_removed_orig; + EXPECT_EQ(to_be_removed.id(), 5); + EXPECT_EQ(to_be_removed_orig.id(), 5); + m.remove_child(to_be_removed); + EXPECT_EQ(to_be_removed.id(), 5); // it is stale now + EXPECT_EQ(to_be_removed_orig.id(), 5); // it is stale now + EXPECT_EQ(m.num_children(), 4); + EXPECT_TRUE(m.at(4).is_seed()); + verify_success(t, [&](Tree const&){ return m.at(4); }); + verify_error(t, [&](Tree const&){ return cm.at(4); }); +} + +TEST(Tree, noderef_at_map) +{ + Tree t = parse_in_arena("{a: 0, b: 1, c: 2, d: 3, e: 4}"); + NodeRef m = t.rootref(); + ConstNodeRef const cm = t.rootref(); + EXPECT_EQ(m.at("a").val(), "0"); + EXPECT_EQ(m.at("b").val(), "1"); + EXPECT_EQ(m.at("c").val(), "2"); + EXPECT_EQ(m.at("d").val(), "3"); + EXPECT_EQ(m.at("e").val(), "4"); + EXPECT_EQ(cm.at("a").val(), "0"); + EXPECT_EQ(cm.at("b").val(), "1"); + EXPECT_EQ(cm.at("c").val(), "2"); + EXPECT_EQ(cm.at("d").val(), "3"); + EXPECT_EQ(cm.at("e").val(), "4"); + // + EXPECT_TRUE(m.at("a") == "0"); + EXPECT_TRUE(m.at("b") == "1"); + EXPECT_TRUE(m.at("c") == "2"); + EXPECT_TRUE(m.at("d") == "3"); + EXPECT_TRUE(m.at("e") == "4"); + EXPECT_TRUE(cm.at("a") == "0"); + EXPECT_TRUE(cm.at("b") == "1"); + EXPECT_TRUE(cm.at("c") == "2"); + EXPECT_TRUE(cm.at("d") == "3"); + EXPECT_TRUE(cm.at("e") == "4"); + // + EXPECT_FALSE(m.at("a") != "0"); + EXPECT_FALSE(m.at("b") != "1"); + EXPECT_FALSE(m.at("c") != "2"); + EXPECT_FALSE(m.at("d") != "3"); + EXPECT_FALSE(m.at("e") != "4"); + EXPECT_FALSE(cm.at("a") != "0"); + EXPECT_FALSE(cm.at("b") != "1"); + EXPECT_FALSE(cm.at("c") != "2"); + EXPECT_FALSE(cm.at("d") != "3"); + EXPECT_FALSE(cm.at("e") != "4"); + // + verify_error(t, [&](Tree const&){ return cm.at(t.capacity()); }); + verify_error(t, [&](Tree const&){ return cm.at(NONE); }); + verify_error(t, [&](Tree const&){ return cm.at(cm.num_children()); }); + verify_error(t, [&](Tree const&){ return cm.at(5); }); + verify_error(t, [&](Tree const&){ return cm.at(6); }); + verify_error(t, [&](Tree const&){ return cm.at("f"); }); + verify_error(t, [&](Tree const&){ return cm.at("g").at("h"); }); + // + verify_error(t, [&](Tree const&){ return m.at(t.capacity()); }); + verify_error(t, [&](Tree const&){ return m.at(NONE); }); + verify_success(t, [&](Tree const&){ return m.at(cm.num_children()); }); + verify_success(t, [&](Tree const&){ return m.at(5); }); + verify_success(t, [&](Tree const&){ return m.at(6); }); + verify_success(t, [&](Tree const&){ return m.at("f"); }); + verify_error(t, [&](Tree const&){ return m.at("g").at("h"); }); + EXPECT_TRUE(m.at(cm.num_children()).is_seed()); + EXPECT_TRUE(m.at(5).is_seed()); + EXPECT_TRUE(m.at(6).is_seed()); + EXPECT_TRUE(m.at("f").is_seed()); } TEST(Tree, relocate)