Commit a865f358 authored by Francis Dupont's avatar Francis Dupont

[5076] Added syntactic contexts

parent e339343d
......@@ -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_);
}
}
......
......@@ -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
......
......@@ -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__");
}
......
......@@ -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
......
......@@ -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
......
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