From: Tomek Mrugalski Date: Sun, 13 Nov 2016 15:31:33 +0000 (+0900) Subject: [5014] Parser type is now configurable. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=036e1d6c287f6dd79c9705f0825613a4e342fc9b;p=thirdparty%2Fkea.git [5014] Parser type is now configurable. --- diff --git a/src/bin/dhcp6/dhcp6_lexer.ll b/src/bin/dhcp6/dhcp6_lexer.ll index 8210b5c965..b120a3c0f4 100644 --- a/src/bin/dhcp6/dhcp6_lexer.ll +++ b/src/bin/dhcp6/dhcp6_lexer.ll @@ -25,6 +25,10 @@ // variable will be useful for logging errors. static isc::dhcp::location loc; +static bool start_token_flag = false; + +static isc::dhcp::Parser6Context::ParserType start_token_value; + // To avoid the call to exit... oops! #define YY_FATAL_ERROR(msg) isc::dhcp::Parser6Context::fatal(msg) %} @@ -81,10 +85,24 @@ JSONString \"{JSONStringCharacter}*\" %% %{ + // This part of the code is copied over to the verbatim to the top + // of the generated yylex function. Explanation: + // http://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html + // Code run each time yylex is called. loc.step(); int comment_start_line = 0; + + if (start_token_flag) { + start_token_flag = false; + switch (start_token_value) { + case Parser6Context::PARSER_DHCP6: + return isc::dhcp::Dhcp6Parser::make_TOPLEVEL_DHCP6(loc); + case Parser6Context::PARSER_GENERIC_JSON: + return isc::dhcp::Dhcp6Parser::make_TOPLEVEL_GENERIC_JSON(loc); + } + } %} #.* ; @@ -247,8 +265,11 @@ null { using namespace isc::dhcp; void -Parser6Context::scanStringBegin() +Parser6Context::scanStringBegin(ParserType parser_type) { + start_token_flag = true; + start_token_value = parser_type; + loc.initialize(&file_); yy_flex_debug = trace_scanning_; YY_BUFFER_STATE buffer; @@ -266,7 +287,11 @@ Parser6Context::scanStringEnd() } void -Parser6Context::scanFileBegin(FILE * f) { +Parser6Context::scanFileBegin(FILE * f, ParserType parser_type) { + + start_token_flag = true; + start_token_value = parser_type; + loc.initialize(&file_); yy_flex_debug = trace_scanning_; YY_BUFFER_STATE buffer; diff --git a/src/bin/dhcp6/dhcp6_parser.yy b/src/bin/dhcp6/dhcp6_parser.yy index 56afaeeb0d..3864cd9b53 100644 --- a/src/bin/dhcp6/dhcp6_parser.yy +++ b/src/bin/dhcp6/dhcp6_parser.yy @@ -115,6 +115,11 @@ using namespace std; OUTPUT "output" DEBUGLEVEL "debuglevel" SEVERITY "severity" + + // Not real tokens, just a way to signal what the parser is expected to + // parse. + TOPLEVEL_DHCP6 + TOPLEVEL_GENERIC_JSON ; %token STRING "constant string" @@ -127,11 +132,15 @@ using namespace std; %printer { yyoutput << $$; } <*>; %% + // The whole grammar starts with a map, because the config file // constists of Dhcp, Logger and DhcpDdns entries in one big { }. // %start map - this will parse everything as generic JSON // %start dhcp6_map - this will parse everything with Dhcp6 syntax checking -%start syntax_map; +%start start; + +start: TOPLEVEL_DHCP6 syntax_map +| TOPLEVEL_GENERIC_JSON map; // ---- generic JSON parser --------------------------------- diff --git a/src/bin/dhcp6/parser_context.cc b/src/bin/dhcp6/parser_context.cc index 07fecd08b2..2cd5c3198e 100644 --- a/src/bin/dhcp6/parser_context.cc +++ b/src/bin/dhcp6/parser_context.cc @@ -25,11 +25,11 @@ Parser6Context::~Parser6Context() } isc::data::ConstElementPtr -Parser6Context::parseString(const std::string& str) +Parser6Context::parseString(const std::string& str, ParserType parser_type) { file_ = ""; string_ = str; - scanStringBegin(); + scanStringBegin(parser_type); isc::dhcp::Dhcp6Parser parser(*this); // Uncomment this to get detailed parser logs. // trace_parsing_ = true; @@ -48,7 +48,7 @@ Parser6Context::parseString(const std::string& str) } isc::data::ConstElementPtr -Parser6Context::parseFile(const std::string& filename) { +Parser6Context::parseFile(const std::string& filename, ParserType parser_type) { ifstream f; f.open(filename); @@ -66,7 +66,7 @@ Parser6Context::parseFile(const std::string& filename) { file_ = filename; - scanStringBegin(); + scanStringBegin(parser_type); isc::dhcp::Dhcp6Parser parser(*this); // Uncomment this to get detailed parser logs. // trace_parsing_ = true; diff --git a/src/bin/dhcp6/parser_context.h b/src/bin/dhcp6/parser_context.h index 1bd2cb0b7b..93630de782 100644 --- a/src/bin/dhcp6/parser_context.h +++ b/src/bin/dhcp6/parser_context.h @@ -34,6 +34,10 @@ public: class Parser6Context { public: + + typedef enum { PARSER_DHCP6, + PARSER_GENERIC_JSON } ParserType; + /// @brief Default constructor. /// /// @param option_universe Option universe: DHCPv4 or DHCPv6. This is used @@ -48,21 +52,23 @@ public: std::vector stack_; /// @brief Method called before scanning starts on a string. - void scanStringBegin(); + void scanStringBegin(ParserType type); /// @brief Method called after the last tokens are scanned from a string. void scanStringEnd(); - void scanFileBegin(FILE * f); + void scanFileBegin(FILE * f, ParserType type); void scanFileEnd(FILE * f); /// @brief Run the parser on the string specified. /// /// @param str string to be written /// @return true on success. - isc::data::ConstElementPtr parseString(const std::string& str); + isc::data::ConstElementPtr parseString(const std::string& str, + ParserType parser_type); - isc::data::ConstElementPtr parseFile(const std::string& filename); + isc::data::ConstElementPtr parseFile(const std::string& filename, + ParserType parser_type); /// @brief The name of the file being parsed. /// Used later to pass the file name to the location tracker. diff --git a/src/bin/dhcp6/tests/parser_unittest.cc b/src/bin/dhcp6/tests/parser_unittest.cc index 84b1be94b9..e438914f22 100644 --- a/src/bin/dhcp6/tests/parser_unittest.cc +++ b/src/bin/dhcp6/tests/parser_unittest.cc @@ -17,32 +17,33 @@ void compareJSON(ConstElementPtr a, ConstElementPtr b, bool print = true) { ASSERT_TRUE(a); ASSERT_TRUE(b); if (print) { - std::cout << a->str() << std::endl; - std::cout << b->str() << std::endl; + std::cout << "JSON A: -----" << endl << a->str() << std::endl; + std::cout << "JSON B: -----" << endl << b->str() << std::endl; + cout << "---------" << endl << endl; } EXPECT_EQ(a->str(), b->str()); } -void testParser(const std::string& txt) { +void testParser(const std::string& txt, Parser6Context::ParserType parser_type) { ElementPtr reference_json; ConstElementPtr test_json; EXPECT_NO_THROW(reference_json = Element::fromJSON(txt, true)); EXPECT_NO_THROW({ Parser6Context ctx; - test_json = ctx.parseString(txt); + test_json = ctx.parseString(txt, parser_type); }); // Now compare if both representations are the same. compareJSON(reference_json, test_json); } -void testParser2(const std::string& txt) { +void testParser2(const std::string& txt, Parser6Context::ParserType parser_type) { ConstElementPtr test_json; EXPECT_NO_THROW({ Parser6Context ctx; - test_json = ctx.parseString(txt); + test_json = ctx.parseString(txt, parser_type); }); /// @todo: Implement actual validation here. since the original /// Element::fromJSON does not support several comment types, we don't @@ -53,35 +54,35 @@ void testParser2(const std::string& txt) { TEST(ParserTest, mapInMap) { string txt = "{ \"Dhcp6\": { \"foo\": 123, \"baz\": 456 } }"; - testParser(txt); + testParser(txt, Parser6Context::PARSER_GENERIC_JSON); } TEST(ParserTest, listInList) { string txt = "{ \"countries\": [ [ \"Britain\", \"Wales\", \"Scotland\" ], " "[ \"Pomorze\", \"Wielkopolska\", \"Tatry\"] ] }"; - testParser(txt); + testParser(txt, Parser6Context::PARSER_GENERIC_JSON); } TEST(ParserTest, nestedMaps) { string txt = "{ \"europe\": { \"UK\": { \"London\": { \"street\": \"221B Baker\" }}}}"; - testParser(txt); + testParser(txt, Parser6Context::PARSER_GENERIC_JSON); } TEST(ParserTest, nestedLists) { string txt = "{ \"unity\": [ \"half\", [ \"quarter\", [ \"eighth\", [ \"sixteenth\" ]]]] }"; - testParser(txt); + testParser(txt, Parser6Context::PARSER_GENERIC_JSON); } TEST(ParserTest, listsInMaps) { string txt = "{ \"constellations\": { \"orion\": [ \"rigel\", \"betelguese\" ], " "\"cygnus\": [ \"deneb\", \"albireo\"] } }"; - testParser(txt); + testParser(txt, Parser6Context::PARSER_GENERIC_JSON); } TEST(ParserTest, mapsInLists) { string txt = "{ \"solar-system\": [ { \"name\": \"earth\", \"gravity\": 1.0 }," " { \"name\": \"mars\", \"gravity\": 0.376 } ] }"; - testParser(txt); + testParser(txt, Parser6Context::PARSER_GENERIC_JSON); } TEST(ParserTest, types) { @@ -91,7 +92,7 @@ TEST(ParserTest, types) { "\"map\": { \"foo\": \"bar\" }," "\"list\": [ 1, 2, 3 ]," "\"null\": null }"; - testParser(txt); + testParser(txt, Parser6Context::PARSER_GENERIC_JSON); } TEST(ParserTest, bashComments) { @@ -111,7 +112,7 @@ TEST(ParserTest, bashComments) { " \"interface\": \"eth0\"" " } ]," "\"valid-lifetime\": 4000 }"; - testParser(txt); + testParser(txt, Parser6Context::PARSER_GENERIC_JSON); } TEST(ParserTest, cComments) { @@ -128,7 +129,7 @@ TEST(ParserTest, cComments) { " \"interface\": \"eth0\"" " } ]," "\"valid-lifetime\": 4000 }"; - testParser2(txt); + testParser2(txt, Parser6Context::PARSER_GENERIC_JSON); } TEST(ParserTest, bashComments2) { @@ -145,7 +146,7 @@ TEST(ParserTest, bashComments2) { " \"interface\": \"eth0\"" " } ]," "\"valid-lifetime\": 4000 }"; - testParser2(txt); + testParser2(txt, Parser6Context::PARSER_GENERIC_JSON); } TEST(ParserTest, multilineComments) { @@ -163,7 +164,7 @@ TEST(ParserTest, multilineComments) { " \"interface\": \"eth0\"" " } ]," "\"valid-lifetime\": 4000 }"; - testParser2(txt); + testParser2(txt, Parser6Context::PARSER_GENERIC_JSON); } @@ -177,7 +178,7 @@ void testFile(const std::string& fname, bool print) { try { Parser6Context ctx; - test_json = ctx.parseFile(fname); + test_json = ctx.parseFile(fname, Parser6Context::PARSER_DHCP6); } catch (const std::exception &x) { cout << "EXCEPTION: " << x.what() << endl; }