From 636c930f5f9685e93c073f6d204f49667e0a6bc7 Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Tue, 28 Nov 2017 01:57:48 +0100 Subject: [PATCH] [5351] Added comment in syntax --- src/bin/dhcp4/dhcp4_lexer.ll | 10 ++++++++ src/bin/dhcp4/dhcp4_parser.cc | 2 +- src/bin/dhcp4/dhcp4_parser.yy | 14 ++++++++++- src/bin/dhcp6/dhcp6_lexer.ll | 11 +++++++++ src/bin/dhcp6/dhcp6_parser.yy | 15 +++++++++++- src/lib/cc/data.cc | 38 +++++++++++++++++++++++++++++- src/lib/cc/data.h | 9 +++++++ src/lib/cc/tests/data_unittests.cc | 23 ++++++++++++++++++ 8 files changed, 118 insertions(+), 4 deletions(-) diff --git a/src/bin/dhcp4/dhcp4_lexer.ll b/src/bin/dhcp4/dhcp4_lexer.ll index c0ea9459df..459420d93d 100644 --- a/src/bin/dhcp4/dhcp4_lexer.ll +++ b/src/bin/dhcp4/dhcp4_lexer.ll @@ -586,6 +586,16 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } +\"comment\" { + switch(driver.ctx_) { + case isc::dhcp::Parser4Context::SUBNET4: + case isc::dhcp::Parser4Context::POOLS: + return isc::dhcp::Dhcp4Parser::make_COMMENT(driver.loc_); + default: + return isc::dhcp::Dhcp4Parser::make_STRING("comment", driver.loc_); + } +} + \"subnet\" { switch(driver.ctx_) { case isc::dhcp::Parser4Context::SUBNET4: diff --git a/src/bin/dhcp4/dhcp4_parser.cc b/src/bin/dhcp4/dhcp4_parser.cc index e77398b20a..80922d4071 100644 --- a/src/bin/dhcp4/dhcp4_parser.cc +++ b/src/bin/dhcp4/dhcp4_parser.cc @@ -2304,7 +2304,7 @@ namespace isc { namespace dhcp { case 377: #line 1346 "dhcp4_parser.yy" // lalr1.cc:859 { - ctx.stack_.back()->set("user-context", yystack_[0].value.as< ElementPtr > ()); + ctx.stack_.back()->combine_set("user-context", yystack_[0].value.as< ElementPtr > ()); ctx.leave(); } #line 2311 "dhcp4_parser.cc" // lalr1.cc:859 diff --git a/src/bin/dhcp4/dhcp4_parser.yy b/src/bin/dhcp4/dhcp4_parser.yy index 66ea540244..5635af158f 100644 --- a/src/bin/dhcp4/dhcp4_parser.yy +++ b/src/bin/dhcp4/dhcp4_parser.yy @@ -109,6 +109,7 @@ using namespace std; POOLS "pools" POOL "pool" USER_CONTEXT "user-context" + COMMENT "comment" SUBNET "subnet" INTERFACE "interface" @@ -920,6 +921,7 @@ subnet4_param: valid_lifetime | subnet_4o6_interface_id | subnet_4o6_subnet | user_context + | comment | unknown_map_entry ; @@ -1330,6 +1332,7 @@ pool_params: pool_param pool_param: pool_entry | option_data_list | user_context + | comment | unknown_map_entry ; @@ -1344,7 +1347,16 @@ pool_entry: POOL { user_context: USER_CONTEXT { ctx.enter(ctx.NO_KEYWORD); } COLON map_value { - ctx.stack_.back()->set("user-context", $4); + ctx.stack_.back()->combine_set("user-context", $4); + ctx.leave(); +}; + +comment: COMMENT { + ctx.enter(ctx.NO_KEYWORD); +} COLON value { + ElementPtr e(new MapElement(ctx.loc2pos(@1))); + e->set("comment", $4); + ctx.stack_.back()->combine_set("user-context", e); ctx.leave(); }; diff --git a/src/bin/dhcp6/dhcp6_lexer.ll b/src/bin/dhcp6/dhcp6_lexer.ll index 6d891067c4..ace108b445 100644 --- a/src/bin/dhcp6/dhcp6_lexer.ll +++ b/src/bin/dhcp6/dhcp6_lexer.ll @@ -817,6 +817,17 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } +\"comment\" { + switch(driver.ctx_) { + case isc::dhcp::Parser6Context::POOLS: + case isc::dhcp::Parser6Context::PD_POOLS: + case isc::dhcp::Parser6Context::SUBNET6: + return isc::dhcp::Dhcp6Parser::make_COMMENT(driver.loc_); + default: + return isc::dhcp::Dhcp6Parser::make_STRING("comment", driver.loc_); + } +} + \"subnet\" { switch(driver.ctx_) { case isc::dhcp::Parser6Context::SUBNET6: diff --git a/src/bin/dhcp6/dhcp6_parser.yy b/src/bin/dhcp6/dhcp6_parser.yy index 5b59e0dc5b..8c3c78957e 100644 --- a/src/bin/dhcp6/dhcp6_parser.yy +++ b/src/bin/dhcp6/dhcp6_parser.yy @@ -99,6 +99,7 @@ using namespace std; EXCLUDED_PREFIX_LEN "excluded-prefix-len" DELEGATED_LEN "delegated-len" USER_CONTEXT "user-context" + COMMENT "comment" SUBNET "subnet" INTERFACE "interface" @@ -908,6 +909,7 @@ subnet6_param: preferred_lifetime | reservation_mode | relay | user_context + | comment | unknown_map_entry ; @@ -1293,6 +1295,7 @@ pool_params: pool_param pool_param: pool_entry | option_data_list | user_context + | comment | unknown_map_entry ; @@ -1307,7 +1310,16 @@ pool_entry: POOL { user_context: USER_CONTEXT { ctx.enter(ctx.NO_KEYWORD); } COLON map_value { - ctx.stack_.back()->set("user-context", $4); + ctx.stack_.back()->combine_set("user-context", $4); + ctx.leave(); +}; + +comment: COMMENT { + ctx.enter(ctx.NO_KEYWORD); +} COLON value { + ElementPtr e(new MapElement(ctx.loc2pos(@1))); + e->set("comment", $4); + ctx.stack_.back()->combine_set("user-context", e); ctx.leave(); }; @@ -1369,6 +1381,7 @@ pd_pool_param: pd_prefix | excluded_prefix | excluded_prefix_len | user_context + | comment | unknown_map_entry ; diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc index 7f043311e9..c188822196 100644 --- a/src/lib/cc/data.cc +++ b/src/lib/cc/data.cc @@ -171,6 +171,11 @@ Element::set(const std::string&, ConstElementPtr) { throwTypeError("set(name, element) called on a non-map Element"); } +void +Element::combine_set(const std::string&, ConstElementPtr) { + throwTypeError("combine_set(name, element) called on a non-map Element"); +} + void Element::remove(const std::string&) { throwTypeError("remove(string) called on a non-map Element"); @@ -902,6 +907,18 @@ MapElement::set(const std::string& key, ConstElementPtr value) { m[key] = value; } +void +MapElement::combine_set(const std::string& key, ConstElementPtr value) { + auto previous = m.find(key); + if (previous == m.end()) { + m[key] = value; + return; + } + ElementPtr mutable_ = boost::const_pointer_cast(previous->second); + combine(mutable_, value); + m[key] = mutable_; +} + bool MapElement::find(const std::string& id, ConstElementPtr& t) const { try { @@ -1313,13 +1330,32 @@ prettyPrint(ConstElementPtr element, std::ostream& out, // open the map out << "{\n"; + bool first = true; + // output comment first + if (element->contains("comment")) { + // add indentation + out << std::string(indent + step, ' '); + // add keyword: + out << "\"comment\": "; + // recursive call + prettyPrint(element->get("comment"), out, indent + step, step); + // it was the first + first = false; + } + // iterate on keyword: value typedef std::map MapType; const MapType& m = element->mapValue(); for (MapType::const_iterator it = m.begin(); it != m.end(); ++it) { + // skip comment + if (it->first == "comment") { + continue; + } // add the separator if not the first item - if (it != m.begin()) { + if (first) { + first = false; + } else { out << ",\n"; } // add indentation diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h index 3f6da3a9f1..c711a4a3b3 100644 --- a/src/lib/cc/data.h +++ b/src/lib/cc/data.h @@ -321,6 +321,12 @@ public: /// @param element The ElementPtr to set at the given key. virtual void set(const std::string& name, ConstElementPtr element); + /// Sets the ElementPtr at the given key, if there was a previous + /// value it is combined with it + /// @param name The key of the Element to set + /// @param element The ElementPtr to set at the given key. + virtual void combine_set(const std::string& name, ConstElementPtr element); + /// Remove the ElementPtr at the given key /// @param name The key of the Element to remove virtual void remove(const std::string& name); @@ -667,6 +673,7 @@ public: } using Element::set; void set(const std::string& key, ConstElementPtr value); + void combine_set(const std::string& key, ConstElementPtr value); using Element::remove; void remove(const std::string& s) { m.erase(s); } bool contains(const std::string& s) const { @@ -773,6 +780,7 @@ bool isEquivalent(ConstElementPtr a, ConstElementPtr b); /// This operator converts the @c ConstElementPtr into a string and /// inserts it into the output stream @c out with an initial /// indentation @c indent and add at each level @c step spaces. +/// For maps if there is a comment property it is printed first. /// /// @param element A @c ConstElementPtr to pretty print /// @param out A @c std::ostream on which the print operation is performed @@ -785,6 +793,7 @@ void prettyPrint(ConstElementPtr element, std::ostream& out, /// /// This operator converts the @c ConstElementPtr into a string with /// an initial indentation @c indent and add at each level @c step spaces. +/// For maps if there is a comment property it is printed first. /// /// @param element A @c ConstElementPtr to pretty print /// @param indent An initial number of spaces to add each new line diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc index dd9402b2c5..d50380fd34 100644 --- a/src/lib/cc/tests/data_unittests.cc +++ b/src/lib/cc/tests/data_unittests.cc @@ -654,6 +654,28 @@ TEST(Element, MapElement) { el->remove("value3"); EXPECT_TRUE(isNull(el->get("value3"))); + EXPECT_TRUE(isNull(el->get("value4"))); + + ElementPtr em = Element::fromJSON("{ \"a\": 1 }"); + el->combine_set("value4", em); + ASSERT_FALSE(isNull(el->get("value4"))); + EXPECT_EQ(em, el->get("value4")); + + em = Element::fromJSON("{ \"a\": 2 }"); + el->combine_set("value4", em); + ASSERT_FALSE(isNull(el->get("value4"))); + ASSERT_EQ(Element::map, el->get("value4")->getType()); + EXPECT_EQ(1, el->get("value4")->size()); + ASSERT_FALSE(isNull(el->get("value4")->get("a"))); + ASSERT_EQ(Element::list, el->get("value4")->get("a")->getType()); + EXPECT_EQ(2, el->get("value4")->get("a")->size()); + ASSERT_EQ(Element::integer, + el->get("value4")->get("a")->get(0)->getType()); + EXPECT_EQ(1, el->get("value4")->get("a")->get(0)->intValue()); + ASSERT_EQ(Element::integer, + el->get("value4")->get("a")->get(1)->getType()); + EXPECT_EQ(2, el->get("value4")->get("a")->get(1)->intValue()); + EXPECT_EQ(el->find("value2/number")->intValue(), 42); EXPECT_TRUE(isNull(el->find("value2/nothing/"))); @@ -1199,6 +1221,7 @@ TEST(Element, prettyPrint) { // default step is 2, order is alphabetic, no \n at the end string text = "{\n" + " \"comment\": \"this is an exception\",\n" " \"boolean\": true,\n" " \"empty-list\": [ ],\n" " \"empty-map\": { },\n" -- 2.47.3