agent_parser.yy 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. /* Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
  2. This Source Code Form is subject to the terms of the Mozilla Public
  3. License, v. 2.0. If a copy of the MPL was not distributed with this
  4. file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. %skeleton "lalr1.cc" /* -*- C++ -*- */
  6. %require "3.0.0"
  7. %defines
  8. %define parser_class_name {AgentParser}
  9. %define api.prefix {agent_}
  10. %define api.token.constructor
  11. %define api.value.type variant
  12. %define api.namespace {isc::agent}
  13. %define parse.assert
  14. %code requires
  15. {
  16. #include <string>
  17. #include <cc/data.h>
  18. #include <boost/lexical_cast.hpp>
  19. #include <agent/parser_context_decl.h>
  20. using namespace isc::agent;
  21. using namespace isc::data;
  22. using namespace std;
  23. }
  24. // The parsing context.
  25. %param { isc::agent::ParserContext& ctx }
  26. %locations
  27. %define parse.trace
  28. %define parse.error verbose
  29. %code
  30. {
  31. #include <agent/parser_context.h>
  32. }
  33. %define api.token.prefix {TOKEN_}
  34. // Tokens in an order which makes sense and related to the intented use.
  35. // Actual regexps for tokens are defined in agent_lexer.ll.
  36. %token
  37. END 0 "end of file"
  38. COMMA ","
  39. COLON ":"
  40. LSQUARE_BRACKET "["
  41. RSQUARE_BRACKET "]"
  42. LCURLY_BRACKET "{"
  43. RCURLY_BRACKET "}"
  44. NULL_TYPE "null"
  45. CONTROL_AGENT "Control-agent"
  46. HTTP_HOST "http-host"
  47. HTTP_PORT "http-port"
  48. CONTROL_SOCKETS "control-sockets"
  49. DHCP4_SERVER "dhcp4-server"
  50. DHCP6_SERVER "dhcp6-server"
  51. D2_SERVER "d2-server"
  52. SOCKET_NAME "socket-name"
  53. SOCKET_TYPE "socket-type"
  54. UNIX "unix"
  55. HOOKS_LIBRARIES "hooks-libraries"
  56. LIBRARY "library"
  57. PARAMETERS "parameters"
  58. LOGGING "Logging"
  59. LOGGERS "loggers"
  60. NAME "name"
  61. OUTPUT_OPTIONS "output_options"
  62. OUTPUT "output"
  63. DEBUGLEVEL "debuglevel"
  64. SEVERITY "severity"
  65. DHCP4 "Dhcp4"
  66. DHCP6 "Dhcp6"
  67. DHCPDDNS "DhcpDdns"
  68. // Not real tokens, just a way to signal what the parser is expected to
  69. // parse. This define the starting point. It either can be full grammar
  70. // (START_AGENT), part of the grammar related to control-agent (START_SUB_AGENT)
  71. // or can be any valid JSON (START_JSON)
  72. START_JSON
  73. START_AGENT
  74. START_SUB_AGENT
  75. ;
  76. %token <std::string> STRING "constant string"
  77. %token <int64_t> INTEGER "integer"
  78. %token <double> FLOAT "floating point"
  79. %token <bool> BOOLEAN "boolean"
  80. %type <ElementPtr> value
  81. %type <ElementPtr> socket_type_value
  82. %printer { yyoutput << $$; } <*>;
  83. %%
  84. // The whole grammar starts with a map, because the config file
  85. // consists of Control-Agent, DhcpX, Logger and DhcpDdns entries in one big { }.
  86. %start start;
  87. // The starting token can be one of those listed below. Note these are
  88. // "fake" tokens. They're produced by the lexer before any input text
  89. // is parsed.
  90. start: START_JSON { ctx.ctx_ = ctx.NO_KEYWORDS; } json
  91. | START_AGENT { ctx.ctx_ = ctx.CONFIG; } agent_syntax_map
  92. | START_SUB_AGENT { ctx.ctx_ = ctx.AGENT; } sub_agent
  93. ;
  94. // This rule defines a "shortcut". Instead of specifying the whole structure
  95. // expected by full grammar, we can tell the parser to start from content of
  96. // the Control-agent. This is very useful for unit-testing, so we don't need
  97. // to repeat the outer map and "Control-agent" map. We can simply provide
  98. // the concents of that map.
  99. sub_agent: LCURLY_BRACKET {
  100. // Parse the Control-agent map
  101. ElementPtr m(new MapElement(ctx.loc2pos(@1)));
  102. ctx.stack_.push_back(m);
  103. } global_params RCURLY_BRACKET {
  104. // parsing completed
  105. };
  106. // --- generic JSON parser -----------------------------------------------------
  107. // json expression can be a value. What value means is defined below.
  108. json: value {
  109. // Push back the JSON value on the stack
  110. ctx.stack_.push_back($1);
  111. };
  112. // Rules for value. This can be one of the primary types allowed in JSON.
  113. value: INTEGER { $$ = ElementPtr(new IntElement($1, ctx.loc2pos(@1))); }
  114. | FLOAT { $$ = ElementPtr(new DoubleElement($1, ctx.loc2pos(@1))); }
  115. | BOOLEAN { $$ = ElementPtr(new BoolElement($1, ctx.loc2pos(@1))); }
  116. | STRING { $$ = ElementPtr(new StringElement($1, ctx.loc2pos(@1))); }
  117. | NULL_TYPE { $$ = ElementPtr(new NullElement(ctx.loc2pos(@1))); }
  118. | map { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
  119. | list_generic { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
  120. ;
  121. // Rule for map. It will start with {, have some content and will end with }.
  122. map: LCURLY_BRACKET {
  123. // This code is executed when we're about to start parsing
  124. // the content of the map
  125. ElementPtr m(new MapElement(ctx.loc2pos(@1)));
  126. ctx.stack_.push_back(m);
  127. } map_content RCURLY_BRACKET {
  128. // map parsing completed. If we ever want to do any wrap up
  129. // (maybe some sanity checking), this would be the best place
  130. // for it.
  131. };
  132. // Rule for map content. In some cases it is allowed to have an empty map,
  133. // so we should say that explicitly. In most cases, though, there will
  134. // be some actual content inside. That's defined by not_empty_map
  135. map_content: %empty // empty map
  136. | not_empty_map
  137. ;
  138. // Rule for content of the map. It can have one of two formats:
  139. // 1) string: value
  140. // 2) non_empty_map , string: value
  141. // The first case covers a single entry, while the second case
  142. // covers all longer lists recursively.
  143. not_empty_map: STRING COLON value {
  144. // map containing a single entry
  145. ctx.stack_.back()->set($1, $3);
  146. }
  147. | not_empty_map COMMA STRING COLON value {
  148. // map consisting of a shorter map followed by
  149. // comma and string:value
  150. ctx.stack_.back()->set($3, $5);
  151. }
  152. ;
  153. list_generic: LSQUARE_BRACKET {
  154. ElementPtr l(new ListElement(ctx.loc2pos(@1)));
  155. ctx.stack_.push_back(l);
  156. } list_content RSQUARE_BRACKET {
  157. };
  158. list_content: %empty // Empty list
  159. | not_empty_list
  160. ;
  161. not_empty_list: value {
  162. // List consisting of a single element.
  163. ctx.stack_.back()->add($1);
  164. }
  165. | not_empty_list COMMA value {
  166. // List ending with , and a value.
  167. ctx.stack_.back()->add($3);
  168. }
  169. ;
  170. // --- generic JSON parser ends here -------------------------------------------
  171. // --- syntax checking parser starts here --------------------------------------
  172. // Unknown keyword in a map. This clever rule can be added to any map
  173. // if you want to have a nice expression printed when unknown (mistyped?)
  174. // parameter is found.
  175. unknown_map_entry: STRING COLON {
  176. const std::string& where = ctx.contextName();
  177. const std::string& keyword = $1;
  178. error(@1,
  179. "got unexpected keyword \"" + keyword + "\" in " + where + " map.");
  180. };
  181. // This defines the top-level { } that holds Control-agent, Dhcp6, Dhcp4,
  182. // DhcpDdns or Logging objects.
  183. agent_syntax_map: LCURLY_BRACKET {
  184. // This code is executed when we're about to start parsing
  185. // the content of the map
  186. ElementPtr m(new MapElement(ctx.loc2pos(@1)));
  187. ctx.stack_.push_back(m);
  188. } global_objects RCURLY_BRACKET {
  189. // map parsing completed. If we ever want to do any wrap up
  190. // (maybe some sanity checking), this would be the best place
  191. // for it.
  192. };
  193. // This represents top-level entries: Control-agent, Logging, possibly others
  194. global_objects: global_object
  195. | global_objects COMMA global_object
  196. ;
  197. // This represents a single top level entry, e.g. Control-agent, Dhcp6 or DhcpDdns.
  198. global_object: agent_object
  199. | logging_object
  200. | dhcp4_json_object
  201. | dhcp6_json_object
  202. | dhcpddns_json_object
  203. | unknown_map_entry
  204. ;
  205. // This define the Control-agent object.
  206. agent_object: CONTROL_AGENT {
  207. // Let's create a MapElement that will represent it, add it to the
  208. // top level map (that's already on the stack) and put the new map
  209. // on the stack as well, so child elements will be able to add
  210. // themselves to it.
  211. ElementPtr m(new MapElement(ctx.loc2pos(@1)));
  212. ctx.stack_.back()->set("Control-agent", m);
  213. ctx.stack_.push_back(m);
  214. ctx.enter(ctx.AGENT);
  215. } COLON LCURLY_BRACKET global_params RCURLY_BRACKET {
  216. // Ok, we're done with parsing control-agent. Let's take the map
  217. // off the stack.
  218. ctx.stack_.pop_back();
  219. ctx.leave();
  220. };
  221. global_params: global_param
  222. | global_params COMMA global_param
  223. ;
  224. // These are the parameters that are allowed in the top-level for
  225. // Dhcp6.
  226. global_param: http_host
  227. | http_port
  228. | control_sockets
  229. | hooks_libraries
  230. | unknown_map_entry
  231. ;
  232. http_host: HTTP_HOST {
  233. ctx.enter(ctx.NO_KEYWORDS);
  234. } COLON STRING {
  235. ElementPtr host(new StringElement($4, ctx.loc2pos(@4)));
  236. ctx.stack_.back()->set("http-host", host);
  237. ctx.leave();
  238. };
  239. http_port: HTTP_PORT COLON INTEGER {
  240. ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
  241. ctx.stack_.back()->set("http-port", prf);
  242. };
  243. // --- hooks-libraries ---------------------------------------------------------
  244. hooks_libraries: HOOKS_LIBRARIES {
  245. ElementPtr l(new ListElement(ctx.loc2pos(@1)));
  246. ctx.stack_.back()->set("hooks-libraries", l);
  247. ctx.stack_.push_back(l);
  248. ctx.enter(ctx.HOOKS_LIBRARIES);
  249. } COLON LSQUARE_BRACKET hooks_libraries_list RSQUARE_BRACKET {
  250. ctx.stack_.pop_back();
  251. ctx.leave();
  252. };
  253. hooks_libraries_list: %empty
  254. | not_empty_hooks_libraries_list
  255. ;
  256. not_empty_hooks_libraries_list: hooks_library
  257. | not_empty_hooks_libraries_list COMMA hooks_library
  258. ;
  259. hooks_library: LCURLY_BRACKET {
  260. ElementPtr m(new MapElement(ctx.loc2pos(@1)));
  261. ctx.stack_.back()->add(m);
  262. ctx.stack_.push_back(m);
  263. } hooks_params RCURLY_BRACKET {
  264. ctx.stack_.pop_back();
  265. };
  266. hooks_params: hooks_param
  267. | hooks_params COMMA hooks_param
  268. | unknown_map_entry
  269. ;
  270. hooks_param: library
  271. | parameters
  272. ;
  273. library: LIBRARY {
  274. ctx.enter(ctx.NO_KEYWORDS);
  275. } COLON STRING {
  276. ElementPtr lib(new StringElement($4, ctx.loc2pos(@4)));
  277. ctx.stack_.back()->set("library", lib);
  278. ctx.leave();
  279. };
  280. parameters: PARAMETERS {
  281. ctx.enter(ctx.NO_KEYWORDS);
  282. } COLON value {
  283. ctx.stack_.back()->set("parameters", $4);
  284. ctx.leave();
  285. };
  286. // --- hooks-libraries end here ------------------------------------------------
  287. // --- control-sockets starts here ---------------------------------------------
  288. control_sockets: CONTROL_SOCKETS COLON LCURLY_BRACKET {
  289. ElementPtr m(new MapElement(ctx.loc2pos(@1)));
  290. ctx.stack_.back()->set("control-sockets", m);
  291. ctx.stack_.push_back(m);
  292. ctx.enter(ctx.CONTROL_SOCKETS);
  293. } control_sockets_params RCURLY_BRACKET {
  294. ctx.stack_.pop_back();
  295. ctx.leave();
  296. };
  297. // This defines what kind of control-sockets parameters we allow.
  298. // Note that empty map is not allowed here, because at least one control socket
  299. // is required.
  300. control_sockets_params: control_socket
  301. | control_sockets_params COMMA control_socket
  302. ;
  303. // We currently support three types of sockets: DHCPv4, DHCPv6 and D2
  304. // (even though D2 socket support is not yet implemented).
  305. control_socket: dhcp4_server_socket
  306. | dhcp6_server_socket
  307. | d2_server_socket
  308. | unknown_map_entry
  309. ;
  310. // That's an entry for dhcp4-server socket.
  311. dhcp4_server_socket: DHCP4_SERVER {
  312. ElementPtr m(new MapElement(ctx.loc2pos(@1)));
  313. ctx.stack_.back()->set("dhcp4-server", m);
  314. ctx.stack_.push_back(m);
  315. ctx.enter(ctx.SERVER);
  316. } COLON LCURLY_BRACKET control_socket_params RCURLY_BRACKET {
  317. ctx.stack_.pop_back();
  318. ctx.leave();
  319. };
  320. // That's an entry for dhcp6-server socket.
  321. dhcp6_server_socket: DHCP6_SERVER {
  322. ElementPtr m(new MapElement(ctx.loc2pos(@1)));
  323. ctx.stack_.back()->set("dhcp6-server", m);
  324. ctx.stack_.push_back(m);
  325. ctx.enter(ctx.SERVER);
  326. } COLON LCURLY_BRACKET control_socket_params RCURLY_BRACKET {
  327. ctx.stack_.pop_back();
  328. ctx.leave();
  329. };
  330. // That's an entry for d2-server socket.
  331. d2_server_socket: D2_SERVER {
  332. ElementPtr m(new MapElement(ctx.loc2pos(@1)));
  333. ctx.stack_.back()->set("d2-server", m);
  334. ctx.stack_.push_back(m);
  335. ctx.enter(ctx.SERVER);
  336. } COLON LCURLY_BRACKET control_socket_params RCURLY_BRACKET {
  337. ctx.stack_.pop_back();
  338. ctx.leave();
  339. };
  340. // Socket parameters consist of one or more parameters.
  341. control_socket_params: control_socket_param
  342. | control_socket_params COMMA control_socket_param
  343. ;
  344. // We currently support two socket parameters: type and name.
  345. control_socket_param: socket_name
  346. | socket_type
  347. ;
  348. // This rule defines socket-name parameter.
  349. socket_name: SOCKET_NAME {
  350. ctx.enter(ctx.NO_KEYWORDS);
  351. } COLON STRING {
  352. ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
  353. ctx.stack_.back()->set("socket-name", name);
  354. ctx.leave();
  355. };
  356. // This rule specifies socket type.
  357. socket_type: SOCKET_TYPE {
  358. ctx.enter(ctx.SOCKET_TYPE);
  359. } COLON socket_type_value {
  360. ctx.stack_.back()->set("socket-type", $4);
  361. ctx.leave();
  362. };
  363. // We currently allow only unix domain sockets
  364. socket_type_value : UNIX { $$ = ElementPtr(new StringElement("unix", ctx.loc2pos(@1))); }
  365. ;
  366. // --- control-sockets end here ------------------------------------------------
  367. // JSON entries for other global objects (Dhcp4,Dhcp6 and DhcpDdns)
  368. dhcp4_json_object: DHCP4 {
  369. ctx.enter(ctx.NO_KEYWORDS);
  370. } COLON value {
  371. ctx.stack_.back()->set("Dhcp4", $4);
  372. ctx.leave();
  373. };
  374. dhcp6_json_object: DHCP6 {
  375. ctx.enter(ctx.NO_KEYWORDS);
  376. } COLON value {
  377. ctx.stack_.back()->set("Dhcp6", $4);
  378. ctx.leave();
  379. };
  380. dhcpddns_json_object: DHCPDDNS {
  381. ctx.enter(ctx.NO_KEYWORDS);
  382. } COLON value {
  383. ctx.stack_.back()->set("DhcpDdns", $4);
  384. ctx.leave();
  385. };
  386. // --- Logging starts here -----------------------------------------------------
  387. // This defines the top level "Logging" object. It parses
  388. // the following "Logging": { ... }. The ... is defined
  389. // by logging_params
  390. logging_object: LOGGING {
  391. ElementPtr m(new MapElement(ctx.loc2pos(@1)));
  392. ctx.stack_.back()->set("Logging", m);
  393. ctx.stack_.push_back(m);
  394. ctx.enter(ctx.LOGGING);
  395. } COLON LCURLY_BRACKET logging_params RCURLY_BRACKET {
  396. ctx.stack_.pop_back();
  397. ctx.leave();
  398. };
  399. // This defines the list of allowed parameters that may appear
  400. // in the top-level Logging object. It can either be a single
  401. // parameter or several parameters separated by commas.
  402. logging_params: logging_param
  403. | logging_params COMMA logging_param
  404. ;
  405. // There's currently only one parameter defined, which is "loggers".
  406. logging_param: loggers;
  407. // "loggers", the only parameter currently defined in "Logging" object,
  408. // is "Loggers": [ ... ].
  409. loggers: LOGGERS {
  410. ElementPtr l(new ListElement(ctx.loc2pos(@1)));
  411. ctx.stack_.back()->set("loggers", l);
  412. ctx.stack_.push_back(l);
  413. ctx.enter(ctx.LOGGERS);
  414. } COLON LSQUARE_BRACKET loggers_entries RSQUARE_BRACKET {
  415. ctx.stack_.pop_back();
  416. ctx.leave();
  417. };
  418. // These are the parameters allowed in loggers: either one logger
  419. // entry or multiple entries separate by commas.
  420. loggers_entries: logger_entry
  421. | loggers_entries COMMA logger_entry
  422. ;
  423. // This defines a single entry defined in loggers in Logging.
  424. logger_entry: LCURLY_BRACKET {
  425. ElementPtr l(new MapElement(ctx.loc2pos(@1)));
  426. ctx.stack_.back()->add(l);
  427. ctx.stack_.push_back(l);
  428. } logger_params RCURLY_BRACKET {
  429. ctx.stack_.pop_back();
  430. };
  431. logger_params: logger_param
  432. | logger_params COMMA logger_param
  433. ;
  434. logger_param: name
  435. | output_options_list
  436. | debuglevel
  437. | severity
  438. | unknown_map_entry
  439. ;
  440. name: NAME {
  441. ctx.enter(ctx.NO_KEYWORDS);
  442. } COLON STRING {
  443. ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
  444. ctx.stack_.back()->set("name", name);
  445. ctx.leave();
  446. };
  447. debuglevel: DEBUGLEVEL COLON INTEGER {
  448. ElementPtr dl(new IntElement($3, ctx.loc2pos(@3)));
  449. ctx.stack_.back()->set("debuglevel", dl);
  450. };
  451. severity: SEVERITY {
  452. ctx.enter(ctx.NO_KEYWORDS);
  453. } COLON STRING {
  454. ElementPtr sev(new StringElement($4, ctx.loc2pos(@4)));
  455. ctx.stack_.back()->set("severity", sev);
  456. ctx.leave();
  457. };
  458. output_options_list: OUTPUT_OPTIONS {
  459. ElementPtr l(new ListElement(ctx.loc2pos(@1)));
  460. ctx.stack_.back()->set("output_options", l);
  461. ctx.stack_.push_back(l);
  462. ctx.enter(ctx.OUTPUT_OPTIONS);
  463. } COLON LSQUARE_BRACKET output_options_list_content RSQUARE_BRACKET {
  464. ctx.stack_.pop_back();
  465. ctx.leave();
  466. };
  467. output_options_list_content: output_entry
  468. | output_options_list_content COMMA output_entry
  469. ;
  470. output_entry: LCURLY_BRACKET {
  471. ElementPtr m(new MapElement(ctx.loc2pos(@1)));
  472. ctx.stack_.back()->add(m);
  473. ctx.stack_.push_back(m);
  474. } output_params RCURLY_BRACKET {
  475. ctx.stack_.pop_back();
  476. };
  477. output_params: output_param
  478. | output_params COMMA output_param
  479. ;
  480. output_param: OUTPUT {
  481. ctx.enter(ctx.NO_KEYWORDS);
  482. } COLON STRING {
  483. ElementPtr sev(new StringElement($4, ctx.loc2pos(@4)));
  484. ctx.stack_.back()->set("output", sev);
  485. ctx.leave();
  486. };
  487. %%
  488. void
  489. isc::agent::AgentParser::error(const location_type& loc,
  490. const std::string& what)
  491. {
  492. ctx.error(loc, what);
  493. }