$(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
%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}
%type <ElementPtr> value
-
%printer { yyoutput << $$; } <*>;
%%
| 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);
}
;
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
#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;
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();
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);
}
};