123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- /* Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- %skeleton "lalr1.cc" /* -*- C++ -*- */
- %require "3.0.0"
- %defines
- %define parser_class_name {AgentParser}
- %define api.prefix {agent_}
- %define api.token.constructor
- %define api.value.type variant
- %define api.namespace {isc::agent}
- %define parse.assert
- %code requires
- {
- #include <string>
- #include <cc/data.h>
- #include <boost/lexical_cast.hpp>
- #include <agent/parser_context_decl.h>
- using namespace isc::agent;
- using namespace isc::data;
- using namespace std;
- }
- // The parsing context.
- %param { isc::agent::ParserContext& ctx }
- %locations
- %define parse.trace
- %define parse.error verbose
- %code
- {
- #include <agent/parser_context.h>
- }
- %define api.token.prefix {TOKEN_}
- // Tokens in an order which makes sense and related to the intented use.
- // Actual regexps for tokens are defined in agent_lexer.ll.
- %token
- END 0 "end of file"
- COMMA ","
- COLON ":"
- LSQUARE_BRACKET "["
- RSQUARE_BRACKET "]"
- LCURLY_BRACKET "{"
- RCURLY_BRACKET "}"
- NULL_TYPE "null"
- CONTROL_AGENT "Control-agent"
- 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"
- LOGGING "Logging"
- LOGGERS "loggers"
- NAME "name"
- OUTPUT_OPTIONS "output_options"
- OUTPUT "output"
- DEBUGLEVEL "debuglevel"
- SEVERITY "severity"
- DHCP4 "Dhcp4"
- DHCP6 "Dhcp6"
- DHCPDDNS "DhcpDdns"
- // Not real tokens, just a way to signal what the parser is expected to
- // parse. This define the starting point. It either can be full grammar
- // (START_AGENT), part of the grammar related to control-agent (START_SUB_AGENT)
- // or can be any valid JSON (START_JSON)
- START_JSON
- START_AGENT
- START_SUB_AGENT
- ;
- %token <std::string> STRING "constant string"
- %token <int64_t> INTEGER "integer"
- %token <double> FLOAT "floating point"
- %token <bool> BOOLEAN "boolean"
- %type <ElementPtr> value
- %type <ElementPtr> socket_type_value
- %printer { yyoutput << $$; } <*>;
- %%
- // The whole grammar starts with a map, because the config file
- // 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.CONFIG; } agent_syntax_map
- | START_SUB_AGENT { ctx.ctx_ = ctx.AGENT; } sub_agent
- ;
- // This rule defines a "shortcut". Instead of specifying the whole structure
- // expected by full grammar, we can tell the parser to start from content of
- // the Control-agent. This is very useful for unit-testing, so we don't need
- // to repeat the outer map and "Control-agent" map. We can simply provide
- // the concents of that map.
- sub_agent: LCURLY_BRACKET {
- // Parse the Control-agent map
- ElementPtr m(new MapElement(ctx.loc2pos(@1)));
- ctx.stack_.push_back(m);
- } global_params RCURLY_BRACKET {
- // parsing completed
- };
- // --- generic JSON parser -----------------------------------------------------
- // json expression can be a value. What value means is defined below.
- json: value {
- // Push back the JSON value on the stack
- ctx.stack_.push_back($1);
- };
- // Rules for value. This can be one of the primary types allowed in JSON.
- value: INTEGER { $$ = ElementPtr(new IntElement($1, ctx.loc2pos(@1))); }
- | FLOAT { $$ = ElementPtr(new DoubleElement($1, ctx.loc2pos(@1))); }
- | BOOLEAN { $$ = ElementPtr(new BoolElement($1, ctx.loc2pos(@1))); }
- | STRING { $$ = ElementPtr(new StringElement($1, ctx.loc2pos(@1))); }
- | NULL_TYPE { $$ = ElementPtr(new NullElement(ctx.loc2pos(@1))); }
- | map { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
- | list_generic { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
- ;
- // Rule for map. It will start with {, have some content and will end with }.
- map: LCURLY_BRACKET {
- // This code is executed when we're about to start parsing
- // the content of the map
- ElementPtr m(new MapElement(ctx.loc2pos(@1)));
- 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.
- };
- // Rule for map content. In some cases it is allowed to have an empty map,
- // so we should say that explicitly. In most cases, though, there will
- // be some actual content inside. That's defined by not_empty_map
- map_content: %empty // empty map
- | not_empty_map
- ;
- // Rule for content of the map. It can have one of two formats:
- // 1) string: value
- // 2) non_empty_map , string: value
- // The first case covers a single entry, while the second case
- // covers all longer lists recursively.
- not_empty_map: STRING COLON value {
- // map containing a single entry
- ctx.stack_.back()->set($1, $3);
- }
- | not_empty_map COMMA STRING COLON value {
- // map consisting of a shorter map followed by
- // comma and string:value
- ctx.stack_.back()->set($3, $5);
- }
- ;
- list_generic: LSQUARE_BRACKET {
- ElementPtr l(new ListElement(ctx.loc2pos(@1)));
- ctx.stack_.push_back(l);
- } list_content RSQUARE_BRACKET {
- };
- list_content: %empty // Empty list
- | not_empty_list
- ;
- not_empty_list: value {
- // List consisting of a single element.
- ctx.stack_.back()->add($1);
- }
- | not_empty_list COMMA value {
- // List ending with , and a value.
- ctx.stack_.back()->add($3);
- }
- ;
- // --- generic JSON parser ends here -------------------------------------------
- // --- syntax checking parser starts here --------------------------------------
- // Unknown keyword in a map. This clever rule can be added to any map
- // 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 " + where + " map.");
- };
- // This defines the top-level { } that holds Control-agent, Dhcp6, Dhcp4,
- // DhcpDdns or Logging objects.
- agent_syntax_map: LCURLY_BRACKET {
- // This code is executed when we're about to start parsing
- // the content of the map
- ElementPtr m(new MapElement(ctx.loc2pos(@1)));
- ctx.stack_.push_back(m);
- } global_objects 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.
- };
- // This represents top-level entries: Control-agent, Logging, possibly others
- global_objects: global_object
- | global_objects COMMA global_object
- ;
- // This represents a single top level entry, e.g. Control-agent, Dhcp6 or DhcpDdns.
- global_object: agent_object
- | logging_object
- | dhcp4_json_object
- | dhcp6_json_object
- | dhcpddns_json_object
- | unknown_map_entry
- ;
- // 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.
- ElementPtr m(new MapElement(ctx.loc2pos(@1)));
- ctx.stack_.back()->set("Control-agent", m);
- ctx.stack_.push_back(m);
- 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.
- ctx.stack_.pop_back();
- ctx.leave();
- };
- global_params: global_param
- | global_params COMMA global_param
- ;
- // These are the parameters that are allowed in the top-level for
- // Dhcp6.
- global_param: http_host
- | http_port
- | control_sockets
- | hooks_libraries
- | unknown_map_entry
- ;
- 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 {
- ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
- ctx.stack_.back()->set("http-port", prf);
- };
- // --- hooks-libraries ---------------------------------------------------------
- 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
- | not_empty_hooks_libraries_list
- ;
- not_empty_hooks_libraries_list: hooks_library
- | not_empty_hooks_libraries_list COMMA hooks_library
- ;
- hooks_library: LCURLY_BRACKET {
- ElementPtr m(new MapElement(ctx.loc2pos(@1)));
- ctx.stack_.back()->add(m);
- ctx.stack_.push_back(m);
- } hooks_params RCURLY_BRACKET {
- ctx.stack_.pop_back();
- };
- hooks_params: hooks_param
- | hooks_params COMMA hooks_param
- | unknown_map_entry
- ;
- hooks_param: library
- | parameters
- ;
- library: LIBRARY {
- ctx.enter(ctx.NO_KEYWORDS);
- } COLON STRING {
- ElementPtr lib(new StringElement($4, ctx.loc2pos(@4)));
- ctx.stack_.back()->set("library", lib);
- ctx.leave();
- };
- parameters: PARAMETERS {
- ctx.enter(ctx.NO_KEYWORDS);
- } COLON value {
- ctx.stack_.back()->set("parameters", $4);
- ctx.leave();
- };
- // --- hooks-libraries end here ------------------------------------------------
- // --- control-sockets starts here ---------------------------------------------
- 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
- ;
- // 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.
- 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.
- 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.
- 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.
- control_socket_params: control_socket_param
- | control_socket_params COMMA control_socket_param
- ;
- // We currently support two socket parameters: type and name.
- control_socket_param: socket_name
- | socket_type
- ;
- // This rule defines socket-name parameter.
- socket_name: SOCKET_NAME {
- ctx.enter(ctx.NO_KEYWORDS);
- } COLON STRING {
- ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
- ctx.stack_.back()->set("socket-name", 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)
- dhcp4_json_object: DHCP4 {
- ctx.enter(ctx.NO_KEYWORDS);
- } COLON value {
- ctx.stack_.back()->set("Dhcp4", $4);
- ctx.leave();
- };
- dhcp6_json_object: DHCP6 {
- ctx.enter(ctx.NO_KEYWORDS);
- } COLON value {
- ctx.stack_.back()->set("Dhcp6", $4);
- ctx.leave();
- };
- dhcpddns_json_object: DHCPDDNS {
- ctx.enter(ctx.NO_KEYWORDS);
- } COLON value {
- ctx.stack_.back()->set("DhcpDdns", $4);
- ctx.leave();
- };
- // --- Logging starts here -----------------------------------------------------
- // This defines the top level "Logging" object. It parses
- // the following "Logging": { ... }. The ... is defined
- // by logging_params
- 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
- // in the top-level Logging object. It can either be a single
- // parameter or several parameters separated by commas.
- logging_params: logging_param
- | logging_params COMMA logging_param
- ;
- // There's currently only one parameter defined, which is "loggers".
- logging_param: loggers;
- // "loggers", the only parameter currently defined in "Logging" object,
- // is "Loggers": [ ... ].
- 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
- // entry or multiple entries separate by commas.
- loggers_entries: logger_entry
- | loggers_entries COMMA logger_entry
- ;
- // This defines a single entry defined in loggers in Logging.
- logger_entry: LCURLY_BRACKET {
- ElementPtr l(new MapElement(ctx.loc2pos(@1)));
- ctx.stack_.back()->add(l);
- ctx.stack_.push_back(l);
- } logger_params RCURLY_BRACKET {
- ctx.stack_.pop_back();
- };
- logger_params: logger_param
- | logger_params COMMA logger_param
- ;
- logger_param: name
- | output_options_list
- | debuglevel
- | severity
- | unknown_map_entry
- ;
- name: NAME {
- ctx.enter(ctx.NO_KEYWORDS);
- } COLON STRING {
- ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
- ctx.stack_.back()->set("name", name);
- ctx.leave();
- };
- debuglevel: DEBUGLEVEL COLON INTEGER {
- ElementPtr dl(new IntElement($3, ctx.loc2pos(@3)));
- ctx.stack_.back()->set("debuglevel", dl);
- };
- severity: SEVERITY {
- ctx.enter(ctx.NO_KEYWORDS);
- } COLON STRING {
- ElementPtr sev(new StringElement($4, ctx.loc2pos(@4)));
- ctx.stack_.back()->set("severity", sev);
- ctx.leave();
- };
- 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
- | output_options_list_content COMMA output_entry
- ;
- output_entry: LCURLY_BRACKET {
- ElementPtr m(new MapElement(ctx.loc2pos(@1)));
- ctx.stack_.back()->add(m);
- ctx.stack_.push_back(m);
- } output_params RCURLY_BRACKET {
- ctx.stack_.pop_back();
- };
- output_params: output_param
- | output_params COMMA output_param
- ;
- output_param: OUTPUT {
- ctx.enter(ctx.NO_KEYWORDS);
- } COLON STRING {
- ElementPtr sev(new StringElement($4, ctx.loc2pos(@4)));
- ctx.stack_.back()->set("output", sev);
- ctx.leave();
- };
- %%
- void
- isc::agent::AgentParser::error(const location_type& loc,
- const std::string& what)
- {
- ctx.error(loc, what);
- }
|