Commit 45abe4f7 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰

[5014] Parser type is now configurable.

parent 3b63aa51
...@@ -25,6 +25,10 @@ ...@@ -25,6 +25,10 @@
// variable will be useful for logging errors. // variable will be useful for logging errors.
static isc::dhcp::location loc; 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! // To avoid the call to exit... oops!
#define YY_FATAL_ERROR(msg) isc::dhcp::Parser6Context::fatal(msg) #define YY_FATAL_ERROR(msg) isc::dhcp::Parser6Context::fatal(msg)
%} %}
...@@ -81,10 +85,24 @@ JSONString \"{JSONStringCharacter}*\" ...@@ -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. // Code run each time yylex is called.
loc.step(); loc.step();
int comment_start_line = 0; 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 { ...@@ -247,8 +265,11 @@ null {
using namespace isc::dhcp; using namespace isc::dhcp;
void void
Parser6Context::scanStringBegin() Parser6Context::scanStringBegin(ParserType parser_type)
{ {
start_token_flag = true;
start_token_value = parser_type;
loc.initialize(&file_); loc.initialize(&file_);
yy_flex_debug = trace_scanning_; yy_flex_debug = trace_scanning_;
YY_BUFFER_STATE buffer; YY_BUFFER_STATE buffer;
...@@ -266,7 +287,11 @@ Parser6Context::scanStringEnd() ...@@ -266,7 +287,11 @@ Parser6Context::scanStringEnd()
} }
void void
Parser6Context::scanFileBegin(FILE * f) { Parser6Context::scanFileBegin(FILE * f, ParserType parser_type) {
start_token_flag = true;
start_token_value = parser_type;
loc.initialize(&file_); loc.initialize(&file_);
yy_flex_debug = trace_scanning_; yy_flex_debug = trace_scanning_;
YY_BUFFER_STATE buffer; YY_BUFFER_STATE buffer;
......
...@@ -115,6 +115,11 @@ using namespace std; ...@@ -115,6 +115,11 @@ using namespace std;
OUTPUT "output" OUTPUT "output"
DEBUGLEVEL "debuglevel" DEBUGLEVEL "debuglevel"
SEVERITY "severity" 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" %token <std::string> STRING "constant string"
...@@ -127,11 +132,15 @@ using namespace std; ...@@ -127,11 +132,15 @@ using namespace std;
%printer { yyoutput << $$; } <*>; %printer { yyoutput << $$; } <*>;
%% %%
// The whole grammar starts with a map, because the config file // The whole grammar starts with a map, because the config file
// constists of Dhcp, Logger and DhcpDdns entries in one big { }. // constists of Dhcp, Logger and DhcpDdns entries in one big { }.
// %start map - this will parse everything as generic JSON // %start map - this will parse everything as generic JSON
// %start dhcp6_map - this will parse everything with Dhcp6 syntax checking // %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 --------------------------------- // ---- generic JSON parser ---------------------------------
......
...@@ -25,11 +25,11 @@ Parser6Context::~Parser6Context() ...@@ -25,11 +25,11 @@ Parser6Context::~Parser6Context()
} }
isc::data::ConstElementPtr isc::data::ConstElementPtr
Parser6Context::parseString(const std::string& str) Parser6Context::parseString(const std::string& str, ParserType parser_type)
{ {
file_ = "<string>"; file_ = "<string>";
string_ = str; string_ = str;
scanStringBegin(); scanStringBegin(parser_type);
isc::dhcp::Dhcp6Parser parser(*this); isc::dhcp::Dhcp6Parser parser(*this);
// Uncomment this to get detailed parser logs. // Uncomment this to get detailed parser logs.
// trace_parsing_ = true; // trace_parsing_ = true;
...@@ -48,7 +48,7 @@ Parser6Context::parseString(const std::string& str) ...@@ -48,7 +48,7 @@ Parser6Context::parseString(const std::string& str)
} }
isc::data::ConstElementPtr isc::data::ConstElementPtr
Parser6Context::parseFile(const std::string& filename) { Parser6Context::parseFile(const std::string& filename, ParserType parser_type) {
ifstream f; ifstream f;
f.open(filename); f.open(filename);
...@@ -66,7 +66,7 @@ Parser6Context::parseFile(const std::string& filename) { ...@@ -66,7 +66,7 @@ Parser6Context::parseFile(const std::string& filename) {
file_ = filename; file_ = filename;
scanStringBegin(); scanStringBegin(parser_type);
isc::dhcp::Dhcp6Parser parser(*this); isc::dhcp::Dhcp6Parser parser(*this);
// Uncomment this to get detailed parser logs. // Uncomment this to get detailed parser logs.
// trace_parsing_ = true; // trace_parsing_ = true;
......
...@@ -34,6 +34,10 @@ public: ...@@ -34,6 +34,10 @@ public:
class Parser6Context class Parser6Context
{ {
public: public:
typedef enum { PARSER_DHCP6,
PARSER_GENERIC_JSON } ParserType;
/// @brief Default constructor. /// @brief Default constructor.
/// ///
/// @param option_universe Option universe: DHCPv4 or DHCPv6. This is used /// @param option_universe Option universe: DHCPv4 or DHCPv6. This is used
...@@ -48,21 +52,23 @@ public: ...@@ -48,21 +52,23 @@ public:
std::vector<isc::data::ElementPtr> stack_; std::vector<isc::data::ElementPtr> stack_;
/// @brief Method called before scanning starts on a string. /// @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. /// @brief Method called after the last tokens are scanned from a string.
void scanStringEnd(); void scanStringEnd();
void scanFileBegin(FILE * f); void scanFileBegin(FILE * f, ParserType type);
void scanFileEnd(FILE * f); void scanFileEnd(FILE * f);
/// @brief Run the parser on the string specified. /// @brief Run the parser on the string specified.
/// ///
/// @param str string to be written /// @param str string to be written
/// @return true on success. /// @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. /// @brief The name of the file being parsed.
/// Used later to pass the file name to the location tracker. /// Used later to pass the file name to the location tracker.
......
...@@ -17,32 +17,33 @@ void compareJSON(ConstElementPtr a, ConstElementPtr b, bool print = true) { ...@@ -17,32 +17,33 @@ void compareJSON(ConstElementPtr a, ConstElementPtr b, bool print = true) {
ASSERT_TRUE(a); ASSERT_TRUE(a);
ASSERT_TRUE(b); ASSERT_TRUE(b);
if (print) { if (print) {
std::cout << a->str() << std::endl; std::cout << "JSON A: -----" << endl << a->str() << std::endl;
std::cout << b->str() << std::endl; std::cout << "JSON B: -----" << endl << b->str() << std::endl;
cout << "---------" << endl << endl;
} }
EXPECT_EQ(a->str(), b->str()); 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; ElementPtr reference_json;
ConstElementPtr test_json; ConstElementPtr test_json;
EXPECT_NO_THROW(reference_json = Element::fromJSON(txt, true)); EXPECT_NO_THROW(reference_json = Element::fromJSON(txt, true));
EXPECT_NO_THROW({ EXPECT_NO_THROW({
Parser6Context ctx; Parser6Context ctx;
test_json = ctx.parseString(txt); test_json = ctx.parseString(txt, parser_type);
}); });
// Now compare if both representations are the same. // Now compare if both representations are the same.
compareJSON(reference_json, test_json); compareJSON(reference_json, test_json);
} }
void testParser2(const std::string& txt) { void testParser2(const std::string& txt, Parser6Context::ParserType parser_type) {
ConstElementPtr test_json; ConstElementPtr test_json;
EXPECT_NO_THROW({ EXPECT_NO_THROW({
Parser6Context ctx; Parser6Context ctx;
test_json = ctx.parseString(txt); test_json = ctx.parseString(txt, parser_type);
}); });
/// @todo: Implement actual validation here. since the original /// @todo: Implement actual validation here. since the original
/// Element::fromJSON does not support several comment types, we don't /// Element::fromJSON does not support several comment types, we don't
...@@ -53,35 +54,35 @@ void testParser2(const std::string& txt) { ...@@ -53,35 +54,35 @@ void testParser2(const std::string& txt) {
TEST(ParserTest, mapInMap) { TEST(ParserTest, mapInMap) {
string txt = "{ \"Dhcp6\": { \"foo\": 123, \"baz\": 456 } }"; string txt = "{ \"Dhcp6\": { \"foo\": 123, \"baz\": 456 } }";
testParser(txt); testParser(txt, Parser6Context::PARSER_GENERIC_JSON);
} }
TEST(ParserTest, listInList) { TEST(ParserTest, listInList) {
string txt = "{ \"countries\": [ [ \"Britain\", \"Wales\", \"Scotland\" ], " string txt = "{ \"countries\": [ [ \"Britain\", \"Wales\", \"Scotland\" ], "
"[ \"Pomorze\", \"Wielkopolska\", \"Tatry\"] ] }"; "[ \"Pomorze\", \"Wielkopolska\", \"Tatry\"] ] }";
testParser(txt); testParser(txt, Parser6Context::PARSER_GENERIC_JSON);
} }
TEST(ParserTest, nestedMaps) { TEST(ParserTest, nestedMaps) {
string txt = "{ \"europe\": { \"UK\": { \"London\": { \"street\": \"221B Baker\" }}}}"; string txt = "{ \"europe\": { \"UK\": { \"London\": { \"street\": \"221B Baker\" }}}}";
testParser(txt); testParser(txt, Parser6Context::PARSER_GENERIC_JSON);
} }
TEST(ParserTest, nestedLists) { TEST(ParserTest, nestedLists) {
string txt = "{ \"unity\": [ \"half\", [ \"quarter\", [ \"eighth\", [ \"sixteenth\" ]]]] }"; string txt = "{ \"unity\": [ \"half\", [ \"quarter\", [ \"eighth\", [ \"sixteenth\" ]]]] }";
testParser(txt); testParser(txt, Parser6Context::PARSER_GENERIC_JSON);
} }
TEST(ParserTest, listsInMaps) { TEST(ParserTest, listsInMaps) {
string txt = "{ \"constellations\": { \"orion\": [ \"rigel\", \"betelguese\" ], " string txt = "{ \"constellations\": { \"orion\": [ \"rigel\", \"betelguese\" ], "
"\"cygnus\": [ \"deneb\", \"albireo\"] } }"; "\"cygnus\": [ \"deneb\", \"albireo\"] } }";
testParser(txt); testParser(txt, Parser6Context::PARSER_GENERIC_JSON);
} }
TEST(ParserTest, mapsInLists) { TEST(ParserTest, mapsInLists) {
string txt = "{ \"solar-system\": [ { \"name\": \"earth\", \"gravity\": 1.0 }," string txt = "{ \"solar-system\": [ { \"name\": \"earth\", \"gravity\": 1.0 },"
" { \"name\": \"mars\", \"gravity\": 0.376 } ] }"; " { \"name\": \"mars\", \"gravity\": 0.376 } ] }";
testParser(txt); testParser(txt, Parser6Context::PARSER_GENERIC_JSON);
} }
TEST(ParserTest, types) { TEST(ParserTest, types) {
...@@ -91,7 +92,7 @@ TEST(ParserTest, types) { ...@@ -91,7 +92,7 @@ TEST(ParserTest, types) {
"\"map\": { \"foo\": \"bar\" }," "\"map\": { \"foo\": \"bar\" },"
"\"list\": [ 1, 2, 3 ]," "\"list\": [ 1, 2, 3 ],"
"\"null\": null }"; "\"null\": null }";
testParser(txt); testParser(txt, Parser6Context::PARSER_GENERIC_JSON);
} }
TEST(ParserTest, bashComments) { TEST(ParserTest, bashComments) {
...@@ -111,7 +112,7 @@ TEST(ParserTest, bashComments) { ...@@ -111,7 +112,7 @@ TEST(ParserTest, bashComments) {
" \"interface\": \"eth0\"" " \"interface\": \"eth0\""
" } ]," " } ],"
"\"valid-lifetime\": 4000 }"; "\"valid-lifetime\": 4000 }";
testParser(txt); testParser(txt, Parser6Context::PARSER_GENERIC_JSON);
} }
TEST(ParserTest, cComments) { TEST(ParserTest, cComments) {
...@@ -128,7 +129,7 @@ TEST(ParserTest, cComments) { ...@@ -128,7 +129,7 @@ TEST(ParserTest, cComments) {
" \"interface\": \"eth0\"" " \"interface\": \"eth0\""
" } ]," " } ],"
"\"valid-lifetime\": 4000 }"; "\"valid-lifetime\": 4000 }";
testParser2(txt); testParser2(txt, Parser6Context::PARSER_GENERIC_JSON);
} }
TEST(ParserTest, bashComments2) { TEST(ParserTest, bashComments2) {
...@@ -145,7 +146,7 @@ TEST(ParserTest, bashComments2) { ...@@ -145,7 +146,7 @@ TEST(ParserTest, bashComments2) {
" \"interface\": \"eth0\"" " \"interface\": \"eth0\""
" } ]," " } ],"
"\"valid-lifetime\": 4000 }"; "\"valid-lifetime\": 4000 }";
testParser2(txt); testParser2(txt, Parser6Context::PARSER_GENERIC_JSON);
} }
TEST(ParserTest, multilineComments) { TEST(ParserTest, multilineComments) {
...@@ -163,7 +164,7 @@ TEST(ParserTest, multilineComments) { ...@@ -163,7 +164,7 @@ TEST(ParserTest, multilineComments) {
" \"interface\": \"eth0\"" " \"interface\": \"eth0\""
" } ]," " } ],"
"\"valid-lifetime\": 4000 }"; "\"valid-lifetime\": 4000 }";
testParser2(txt); testParser2(txt, Parser6Context::PARSER_GENERIC_JSON);
} }
...@@ -177,7 +178,7 @@ void testFile(const std::string& fname, bool print) { ...@@ -177,7 +178,7 @@ void testFile(const std::string& fname, bool print) {
try { try {
Parser6Context ctx; Parser6Context ctx;
test_json = ctx.parseFile(fname); test_json = ctx.parseFile(fname, Parser6Context::PARSER_DHCP6);
} catch (const std::exception &x) { } catch (const std::exception &x) {
cout << "EXCEPTION: " << x.what() << endl; cout << "EXCEPTION: " << x.what() << endl;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment