Browse Source

[5014] Parser type is now configurable.

Tomek Mrugalski 8 years ago
parent
commit
45abe4f73c

+ 27 - 2
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;

+ 10 - 1
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 <std::string> 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 ---------------------------------
 

+ 4 - 4
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>";
     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;

+ 10 - 4
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<isc::data::ElementPtr> 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.

+ 19 - 18
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;
     }