Commit 32a96adb authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰

[5014] Parser improved, unit-tests added.

parent ebd458e3
......@@ -125,7 +125,7 @@ location.hh position.hh stack.hh dhcp6_parser.cc dhcp6_parser.h: dhcp6_parser.yy
$(YACC) --defines=dhcp6_parser.h -o dhcp6_parser.cc dhcp6_parser.yy
dhcp6_lexer.cc: dhcp6_lexer.ll
$(LEX) -o dhcp6_lexer.cc dhcp6_lexer.ll
$(LEX) --prefix parser6_ -o dhcp6_lexer.cc dhcp6_lexer.ll
else
......
......@@ -8,6 +8,7 @@
%require "3.0.0"
%defines
%define parser_class_name {Dhcp6Parser}
%define api.prefix {parser6_}
%define api.token.constructor
%define api.value.type variant
%define api.namespace {isc::dhcp}
......@@ -55,7 +56,6 @@ using namespace std;
%type <ElementPtr> value
%printer { yyoutput << $$; } <*>;
%%
......@@ -69,37 +69,50 @@ value : INTEGER { $$ = ElementPtr(new IntElement($1)); }
| BOOLEAN { $$ = ElementPtr(new BoolElement($1)); }
| STRING { $$ = ElementPtr(new StringElement($1)); }
| NULL_TYPE { $$ = ElementPtr(new NullElement()); }
| map { $$ = ElementPtr(new MapElement()); }
| list { $$ = ElementPtr(new ListElement()); }
| map { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
| list { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
;
map: LCURLY_BRACKET {
ctx.stack_.push_back(ElementPtr(new MapElement()));
} map_content RCURLY_BRACKET {
ctx.stack_.pop_back();
};
// This code is executed when we're about to start parsing
// the content of the map
ElementPtr m(new MapElement());
ctx.stack_.push_back(m);
} map_content RCURLY_BRACKET {
// map parsing completed. If we ever want to do any wrap up
// (maybe some sanity checking), this would be the best place
// for it.
};
// Assignments rule
map_content: { /* do nothing, it's an empty map */ }
| STRING COLON value {
(*ctx.stack_.end())->set($1, $3);
// map containing a single entry
ctx.stack_.back()->set($1, $3);
}
| map COMMA STRING COLON value {
(*ctx.stack_.end())->set($3, $5);
| map_content COMMA STRING COLON value {
// map consisting of a shorter map followed by comma and string:value
ctx.stack_.back()->set($3, $5);
}
;
list: LSQUARE_BRACKET list_content RSQUARE_BRACKET { };
list: LSQUARE_BRACKET {
// List parsing about to start
ElementPtr l(new ListElement());
ctx.stack_.push_back(l);
} list_content RSQUARE_BRACKET {
// list parsing complete. Put any sanity checking here
};
list_content: { /* do nothing, it's an empty list */ }
| value {
// List consisting of a single element.
(*ctx.stack_.end())->add($1);
ctx.stack_.back()->add($1);
}
| list COMMA value {
| list_content COMMA value {
// List ending with , and a value.
(*ctx.stack_.end())->add($3);
ctx.stack_.back()->add($3);
}
;
......
......@@ -31,13 +31,20 @@ Parser6Context::parseString(const std::string& str)
string_ = str;
scanStringBegin();
isc::dhcp::Dhcp6Parser parser(*this);
// Uncomment this to get detailed parser logs.
// trace_parsing_ = true;
parser.set_debug_level(trace_parsing_);
int res = parser.parse();
if (res != 0) {
// @todo: handle exception here
}
scanStringEnd();
return (*stack_.end());
if (stack_.size() == 1) {
return (stack_[0]);
} else {
isc_throw(BadValue, "Expected exactly one terminal Element, found "
<< stack_.size());
}
}
void
......
......@@ -14,7 +14,7 @@
#include <exceptions/exceptions.h>
// Tell Flex the lexer's prototype ...
#define YY_DECL isc::dhcp::Dhcp6Parser::symbol_type yylex (Parser6Context& driver)
#define YY_DECL isc::dhcp::Dhcp6Parser::symbol_type parser6_lex (Parser6Context& driver)
// ... and declare it for the parser's sake.
YY_DECL;
......@@ -45,7 +45,7 @@ public:
virtual ~Parser6Context();
/// @brief JSON elements being parsed.
std::vector<ElementPtr> stack_;
std::vector<isc::data::ElementPtr> stack_;
/// @brief Method called before scanning starts on a string.
void scanStringBegin();
......
......@@ -13,15 +13,67 @@ using namespace std;
namespace {
TEST(ParserTest, basic) {
void compareJSON(ConstElementPtr a, ConstElementPtr b) {
std::cout << a->str() << std::endl;
std::cout << b->str() << std::endl;
EXPECT_EQ(a->str(), b->str());
}
void testParser(const std::string& txt) {
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);
});
// Now compare if both representations are the same.
compareJSON(reference_json, test_json);
}
TEST(ParserTest, mapInMap) {
string txt = "{ \"Dhcp6\": { \"foo\": 123, \"baz\": 456 } }";
testParser(txt);
}
Parser6Context ctx;
TEST(ParserTest, listInList) {
string txt = "{ \"countries\": [ [ \"Britain\", \"Wales\", \"Scotland\" ], "
"[ \"Pomorze\", \"Wielkopolska\", \"Tatry\"] ] }";
testParser(txt);
}
TEST(ParserTest, nestedMaps) {
string txt = "{ \"europe\": { \"UK\": { \"London\": { \"street\": \"221B Baker\" }}}}";
testParser(txt);
}
string txt = "{ \"Dhcp6\": { } }";
TEST(ParserTest, nestedLists) {
string txt = "{ \"unity\": [ \"half\", [ \"quarter\", [ \"eighth\", [ \"sixteenth\" ]]]] }";
testParser(txt);
}
ConstElementPtr json = ctx.parseString(txt);
TEST(ParserTest, listsInMaps) {
string txt = "{ \"constellations\": { \"orion\": [ \"rigel\", \"betelguese\" ], "
"\"cygnus\": [ \"deneb\", \"albireo\"] } }";
testParser(txt);
}
TEST(ParserTest, mapsInLists) {
string txt = "{ \"solar-system\": [ { \"name\": \"earth\", \"gravity\": 1.0 },"
" { \"name\": \"mars\", \"gravity\": 0.376 } ] }";
testParser(txt);
}
ASSERT_TRUE(json);
TEST(ParserTest, types) {
string txt = "{ \"string\": \"foo\","
"\"integer\": 42,"
"\"boolean\": true,"
"\"map\": { \"foo\": \"bar\" },"
"\"list\": [ 1, 2, 3 ],"
"\"null\": null }";
testParser(txt);
}
};
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