Browse Source

[5076] Added syntactic contexts

Francis Dupont 8 years ago
parent
commit
a865f3588a

+ 117 - 90
src/bin/agent/agent_lexer.ll

@@ -168,182 +168,209 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
 
 
 \"Control-agent\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::CONFIG:
         return AgentParser::make_CONTROL_AGENT(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("Control-agent", driver.loc_);
     }
 }
 
 \"http-host\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::AGENT:
         return AgentParser::make_HTTP_HOST(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("http-host", driver.loc_);
     }
 }
 
 \"http-port\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::AGENT:
         return AgentParser::make_HTTP_PORT(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("http-port", driver.loc_);
     }
 }
 
+\"control-sockets\" {
+    switch(driver.ctx_) {
+    case ParserContext::AGENT:
+        return AgentParser::make_CONTROL_SOCKETS(driver.loc_);
+    default:
+        return AgentParser::make_STRING("control-sockets", driver.loc_);
+    }
+}
+
 \"dhcp4-server\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::CONTROL_SOCKETS:
         return AgentParser::make_DHCP4_SERVER(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("dhcp4-server", driver.loc_);
     }
 }
 
 \"dhcp6-server\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::CONTROL_SOCKETS:
         return AgentParser::make_DHCP6_SERVER(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("dhcp6-server", driver.loc_);
     }
 }
 
 \"d2-server\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::CONTROL_SOCKETS:
         return AgentParser::make_D2_SERVER(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("d2-server", driver.loc_);
     }
 }
 
-(\"unix\") {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+\"socket-name\" {
+    switch(driver.ctx_) {
+    case ParserContext::SERVER:
+        return AgentParser::make_SOCKET_NAME(driver.loc_);
+    default:
+        return AgentParser::make_STRING("socket-name", driver.loc_);
+    }
+}
+
+\"socket-type\" {
+    switch(driver.ctx_) {
+    case ParserContext::SERVER:
+        return AgentParser::make_SOCKET_TYPE(driver.loc_);
+    default:
+        return AgentParser::make_STRING("socket-type", driver.loc_);
+    }
+}
+
+\"unix\" {
+    switch(driver.ctx_) {
+    case ParserContext::SOCKET_TYPE:
         return AgentParser::make_UNIX(driver.loc_);
+    default:
+        return AgentParser::make_STRING("unix", driver.loc_);
     }
-    std::string tmp(yytext+1);
-    tmp.resize(tmp.size() - 1);
-    return AgentParser::make_STRING(tmp, driver.loc_);
 }
 
-\"name\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
-        return AgentParser::make_NAME(driver.loc_);
-    } else {
-        return AgentParser::make_STRING("name", driver.loc_);
+\"hooks-libraries\" {
+    switch(driver.ctx_) {
+    case ParserContext::AGENT:
+        return AgentParser::make_HOOKS_LIBRARIES(driver.loc_);
+    default:
+        return AgentParser::make_STRING("hooks-libraries", driver.loc_);
     }
 }
 
+\"library\" {
+    switch(driver.ctx_) {
+    case ParserContext::HOOKS_LIBRARIES:
+        return AgentParser::make_LIBRARY(driver.loc_);
+    default:
+        return AgentParser::make_STRING("library", driver.loc_);
+    }
+}
+
+\"parameters\" {
+    switch(driver.ctx_) {
+    case ParserContext::HOOKS_LIBRARIES:
+        return AgentParser::make_PARAMETERS(driver.loc_);
+    default:
+        return AgentParser::make_STRING("parameters", driver.loc_);
+    }
+}
 
 \"Logging\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::CONFIG:
         return AgentParser::make_LOGGING(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("Logging", driver.loc_);
     }
 }
 
 \"loggers\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::LOGGING:
         return AgentParser::make_LOGGERS(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("loggers", driver.loc_);
     }
 }
 
+\"name\" {
+    switch(driver.ctx_) {
+    case ParserContext::LOGGERS:
+        return AgentParser::make_NAME(driver.loc_);
+    default:
+        return AgentParser::make_STRING("name", driver.loc_);
+    }
+}
+
 \"output_options\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::LOGGERS:
         return AgentParser::make_OUTPUT_OPTIONS(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("output_options", driver.loc_);
     }
 }
 
 \"output\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::OUTPUT_OPTIONS:
         return AgentParser::make_OUTPUT(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("output", driver.loc_);
     }
 }
 
 \"debuglevel\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::LOGGERS:
         return AgentParser::make_DEBUGLEVEL(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("debuglevel", driver.loc_);
     }
 }
 
 \"severity\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::LOGGERS:
         return AgentParser::make_SEVERITY(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("severity", driver.loc_);
     }
 }
 
-\"hooks-libraries\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
-        return AgentParser::make_HOOKS_LIBRARIES(driver.loc_);
-    } else {
-        return AgentParser::make_STRING("hooks-libraries", driver.loc_);
-    }
-}
-
-
-\"parameters\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
-        return AgentParser::make_PARAMETERS(driver.loc_);
-    } else {
-        return AgentParser::make_STRING("parameters", driver.loc_);
-    }
-}
-
-\"library\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
-        return AgentParser::make_LIBRARY(driver.loc_);
-    } else {
-        return AgentParser::make_STRING("library", driver.loc_);
-    }
-}
-
-
-\"control-sockets\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
-        return AgentParser::make_CONTROL_SOCKETS(driver.loc_);
-    } else {
-        return AgentParser::make_STRING("control-sockets", driver.loc_);
-    }
-}
-
-\"socket-type\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
-        return AgentParser::make_SOCKET_TYPE(driver.loc_);
-    } else {
-        return AgentParser::make_STRING("socket-type", driver.loc_);
-    }
-}
-
-\"socket-name\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
-        return AgentParser::make_SOCKET_NAME(driver.loc_);
-    } else {
-        return AgentParser::make_STRING("socket-name", driver.loc_);
-    }
-}
-
 \"Dhcp4\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
+    switch(driver.ctx_) {
+    case ParserContext::CONFIG:
         return AgentParser::make_DHCP4(driver.loc_);
-    } else {
+    default:
         return AgentParser::make_STRING("Dhcp4", driver.loc_);
     }
 }
 
 \"Dhcp6\" {
-    if (driver.ctx_ != ParserContext::NO_KEYWORDS) {
-        return AgentParser::make_DHCP4(driver.loc_);
-    } else {
-        return AgentParser::make_STRING("Dhcp4", driver.loc_);
+    switch(driver.ctx_) {
+    case ParserContext::CONFIG:
+        return AgentParser::make_DHCP6(driver.loc_);
+    default:
+        return AgentParser::make_STRING("Dhcp6", driver.loc_);
+    }
+}
+
+\"DhcpDdns\" {
+    switch(driver.ctx_) {
+    case ParserContext::CONFIG:
+        return AgentParser::make_DHCPDDNS(driver.loc_);
+    default:
+        return AgentParser::make_STRING("DhcpDdns", driver.loc_);
     }
 }
 

+ 57 - 36
src/bin/agent/agent_parser.yy

@@ -49,29 +49,28 @@ using namespace std;
   NULL_TYPE "null"
 
   CONTROL_AGENT "Control-agent"
-  CONTROL_SOCKETS "control-sockets"
   HTTP_HOST "http-host"
   HTTP_PORT "http-port"
+
+  CONTROL_SOCKETS "control-sockets"
   DHCP4_SERVER "dhcp4-server"
   DHCP6_SERVER "dhcp6-server"
   D2_SERVER "d2-server"
+  SOCKET_NAME "socket-name"
+  SOCKET_TYPE "socket-type"
+  UNIX "unix"
 
   HOOKS_LIBRARIES "hooks-libraries"
   LIBRARY "library"
   PARAMETERS "parameters"
 
-  SOCKET_TYPE "socket-type"
-  SOCKET_NAME "socket-name"
-
-  UNIX "unix"
-
   LOGGING "Logging"
   LOGGERS "loggers"
+  NAME "name"
   OUTPUT_OPTIONS "output_options"
   OUTPUT "output"
   DEBUGLEVEL "debuglevel"
   SEVERITY "severity"
-  NAME "name"
 
   DHCP4 "Dhcp4"
   DHCP6 "Dhcp6"
@@ -99,15 +98,15 @@ using namespace std;
 %%
 
 // The whole grammar starts with a map, because the config file
-// constists of Control-Agent, DhcpX, Logger and DhcpDdns entries in one big { }.
+// consists of Control-Agent, DhcpX, Logger and DhcpDdns entries in one big { }.
 %start start;
 
 // The starting token can be one of those listed below. Note these are
 // "fake" tokens. They're produced by the lexer before any input text
 // is parsed.
 start: START_JSON      { ctx.ctx_ = ctx.NO_KEYWORDS; } json
-     | START_AGENT     { ctx.ctx_ = ctx.KEYWORDS; } agent_syntax_map
-     | START_SUB_AGENT { ctx.ctx_ = ctx.KEYWORDS; } sub_agent 
+     | START_AGENT     { ctx.ctx_ = ctx.CONFIG; } agent_syntax_map
+     | START_SUB_AGENT { ctx.ctx_ = ctx.AGENT; } sub_agent 
      ;
 
 // This rule defines a "shortcut". Instead of specifying the whole structure
@@ -204,9 +203,10 @@ not_empty_list: value {
 // if you want to have a nice expression printed when unknown (mistyped?)
 // parameter is found.
 unknown_map_entry: STRING COLON {
+    const std::string& where = ctx.contextName();
     const std::string& keyword = $1;
     error(@1,
-          "got unexpected keyword \"" + keyword + "\" in map.");
+          "got unexpected keyword \"" + keyword + "\" in " + where + " map.");
 };
 
 // This defines the top-level { } that holds Control-agent, Dhcp6, Dhcp4,
@@ -238,20 +238,19 @@ global_object: agent_object
 
 // This define the Control-agent object.
 agent_object: CONTROL_AGENT {
-    // Let's create a MapElement that will represent it, add it to the top level
-    // map (that's already on the stack) and put the new map on the stack as well,
-    // so child elements will be able to add themselves to it.
+
+    // Let's create a MapElement that will represent it, add it to the
+    // top level map (that's already on the stack) and put the new map
+    // on the stack as well, so child elements will be able to add
+    // themselves to it.
     ElementPtr m(new MapElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("Control-agent", m);
     ctx.stack_.push_back(m);
-
-    // And tell the lexer that we definitely want keywords to be recognized.
-    ctx.enter(ctx.KEYWORDS);
+    ctx.enter(ctx.AGENT);
 } COLON LCURLY_BRACKET global_params RCURLY_BRACKET {
-    // Ok, we're done with parsing control-agent. Let's take the map off the stack.
+    // Ok, we're done with parsing control-agent. Let's take the map
+    // off the stack.
     ctx.stack_.pop_back();
-
-    // And tell the lexer to return to its previous state (probably KEYWORDS as well)
     ctx.leave();
 };
 
@@ -268,9 +267,12 @@ global_param: http_host
             | unknown_map_entry
             ;
 
-http_host: HTTP_HOST COLON STRING {
-    ElementPtr host(new StringElement($3, ctx.loc2pos(@3)));
+http_host: HTTP_HOST {
+    ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+    ElementPtr host(new StringElement($4, ctx.loc2pos(@4)));
     ctx.stack_.back()->set("http-host", host);
+    ctx.leave();
 };
 
 http_port: HTTP_PORT COLON INTEGER {
@@ -283,8 +285,10 @@ hooks_libraries: HOOKS_LIBRARIES {
     ElementPtr l(new ListElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("hooks-libraries", l);
     ctx.stack_.push_back(l);
+    ctx.enter(ctx.HOOKS_LIBRARIES);
 } COLON LSQUARE_BRACKET hooks_libraries_list RSQUARE_BRACKET {
     ctx.stack_.pop_back();
+    ctx.leave();
 };
 
 hooks_libraries_list: %empty
@@ -334,23 +338,25 @@ control_sockets: CONTROL_SOCKETS COLON LCURLY_BRACKET {
     ElementPtr m(new MapElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("control-sockets", m);
     ctx.stack_.push_back(m);
+    ctx.enter(ctx.CONTROL_SOCKETS);
 } control_sockets_params RCURLY_BRACKET {
     ctx.stack_.pop_back();
+    ctx.leave();
 };
 
 // This defines what kind of control-sockets parameters we allow.
 // Note that empty map is not allowed here, because at least one control socket
 // is required.
 control_sockets_params: control_socket
-               | control_sockets_params COMMA control_socket
-               | unknown_map_entry
-               ;
+                      | control_sockets_params COMMA control_socket
+                      ;
 
 // We currently support three types of sockets: DHCPv4, DHCPv6 and D2
 // (even though D2 socket support is not yet implemented).
 control_socket: dhcp4_server_socket
               | dhcp6_server_socket
               | d2_server_socket
+              | unknown_map_entry
               ;
 
 // That's an entry for dhcp4-server socket.
@@ -358,8 +364,10 @@ dhcp4_server_socket: DHCP4_SERVER {
     ElementPtr m(new MapElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("dhcp4-server", m);
     ctx.stack_.push_back(m);
+    ctx.enter(ctx.SERVER);
 } COLON LCURLY_BRACKET control_socket_params RCURLY_BRACKET {
     ctx.stack_.pop_back();
+    ctx.leave();
 };
 
 // That's an entry for dhcp6-server socket.
@@ -367,8 +375,10 @@ dhcp6_server_socket: DHCP6_SERVER {
     ElementPtr m(new MapElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("dhcp6-server", m);
     ctx.stack_.push_back(m);
+    ctx.enter(ctx.SERVER);
 } COLON LCURLY_BRACKET control_socket_params RCURLY_BRACKET {
     ctx.stack_.pop_back();
+    ctx.leave();
 };
 
 // That's an entry for d2-server socket.
@@ -376,8 +386,10 @@ d2_server_socket: D2_SERVER {
     ElementPtr m(new MapElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("d2-server", m);
     ctx.stack_.push_back(m);
+    ctx.enter(ctx.SERVER);
 } COLON LCURLY_BRACKET control_socket_params RCURLY_BRACKET {
     ctx.stack_.pop_back();
+    ctx.leave();
 };
 
 // Socket parameters consist of one or more parameters.
@@ -386,19 +398,10 @@ control_socket_params: control_socket_param
                      ;
 
 // We currently support two socket parameters: type and name.
-control_socket_param: socket_type
-                    | socket_name
+control_socket_param: socket_name
+                    | socket_type
                     ;
 
-// This rule specifies socket type.
-socket_type: SOCKET_TYPE {
-} COLON socket_type_value {
-    ctx.stack_.back()->set("socket-type", $4);
-};
-
-// We currently allow only unix domain sockets
-socket_type_value : UNIX { $$ = ElementPtr(new StringElement("unix", ctx.loc2pos(@1))); }
-
 // This rule defines socket-name parameter.
 socket_name: SOCKET_NAME {
     ctx.enter(ctx.NO_KEYWORDS);
@@ -408,6 +411,18 @@ socket_name: SOCKET_NAME {
     ctx.leave();
 };
 
+// This rule specifies socket type.
+socket_type: SOCKET_TYPE {
+    ctx.enter(ctx.SOCKET_TYPE);
+} COLON socket_type_value {
+    ctx.stack_.back()->set("socket-type", $4);
+    ctx.leave();
+};
+
+// We currently allow only unix domain sockets
+socket_type_value : UNIX { $$ = ElementPtr(new StringElement("unix", ctx.loc2pos(@1))); }
+                  ;
+
 // --- control-sockets end here ------------------------------------------------
 
 // JSON entries for other global objects (Dhcp4,Dhcp6 and DhcpDdns)
@@ -441,8 +456,10 @@ logging_object: LOGGING {
     ElementPtr m(new MapElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("Logging", m);
     ctx.stack_.push_back(m);
+    ctx.enter(ctx.LOGGING);
 } COLON LCURLY_BRACKET logging_params RCURLY_BRACKET {
     ctx.stack_.pop_back();
+    ctx.leave();
 };
 
 // This defines the list of allowed parameters that may appear
@@ -461,8 +478,10 @@ loggers: LOGGERS {
     ElementPtr l(new ListElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("loggers", l);
     ctx.stack_.push_back(l);
+    ctx.enter(ctx.LOGGERS);
 }  COLON LSQUARE_BRACKET loggers_entries RSQUARE_BRACKET {
     ctx.stack_.pop_back();
+    ctx.leave();
 };
 
 // These are the parameters allowed in loggers: either one logger
@@ -516,8 +535,10 @@ output_options_list: OUTPUT_OPTIONS {
     ElementPtr l(new ListElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("output_options", l);
     ctx.stack_.push_back(l);
+    ctx.enter(ctx.OUTPUT_OPTIONS);
 } COLON LSQUARE_BRACKET output_options_list_content RSQUARE_BRACKET {
     ctx.stack_.pop_back();
+    ctx.leave();
 };
 
 output_options_list_content: output_entry

+ 19 - 3
src/bin/agent/parser_context.cc

@@ -116,9 +116,25 @@ ParserContext::contextName()
 {
     switch (ctx_) {
     case NO_KEYWORDS:
-        return ("no keywords");
-    case KEYWORDS:
-        return ("keywords");
+        return ("__no keywords__");
+    case CONFIG:
+        return ("toplevel");
+    case AGENT:
+        return ("Control-agent");
+    case LOGGING:
+        return ("Logging");
+    case CONTROL_SOCKETS:
+        return ("control-sockets");
+    case SERVER:
+        return ("xxx-server");
+    case SOCKET_TYPE:
+        return ("socket-type");
+    case HOOKS_LIBRARIES:
+        return ("hooks-librairies");
+    case LOGGERS:
+        return ("loggers");
+    case OUTPUT_OPTIONS:
+        return ("output-options");
     default:
         return ("__unknown__");
     }

+ 26 - 1
src/bin/agent/parser_context.h

@@ -152,8 +152,33 @@ public:
         ///< This one is used in pure JSON mode.
         NO_KEYWORDS,
 
+        ///< Used while parsing top level (that contains Control-agent, Logging and others)
+        CONFIG,
+
         ///< Used while parsing content of Agent.
-        KEYWORDS
+        AGENT,
+
+        ///< Used while parsing content of Logging.
+        LOGGING,
+
+        ///< Used while parsing Control-agent/control-sockets.
+        CONTROL_SOCKETS,
+
+        ///< Used while parsing Control-agent/control-socket/*-server.
+        SERVER,
+
+        ///< Used while parsing Control-agent/control-socket/*-server/socket-type.
+        SOCKET_TYPE,
+
+        ///< Used while parsing Control-agent/hooks-libraries.
+        HOOKS_LIBRARIES,
+
+        ///< Used while parsing Logging/loggers structures.
+        LOGGERS,
+
+        ///< Used while parsing Logging/loggers/output_options structures.
+        OUTPUT_OPTIONS
+
     } LexerContext;
 
     /// @brief File name

+ 2 - 2
src/bin/agent/tests/parser_unittest.cc

@@ -503,7 +503,7 @@ TEST(ParserTest, errors) {
     testError("{ \"foo\":null }\n",
               ParserContext::PARSER_AGENT,
               "<string>:1.3-7: got unexpected keyword "
-              "\"foo\" in map.");
+              "\"foo\" in toplevel map.");
     testError("{ \"Control-agent\" }\n",
               ParserContext::PARSER_AGENT,
               "<string>:1.19: syntax error, unexpected }, "
@@ -543,7 +543,7 @@ TEST(ParserTest, errors) {
               " \"topping\": \"Mozarella\" }}\n",
               ParserContext::PARSER_AGENT,
               "<string>:2.2-10: got unexpected keyword "
-              "\"topping\" in map.");
+              "\"topping\" in Control-agent map.");
 }
 
 // Check unicode escapes