parser.yy 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. /* Copyright (C) 2015-2016 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 {EvalParser}
  9. %define api.token.constructor
  10. %define api.value.type variant
  11. %define api.namespace {isc::eval}
  12. %define parse.assert
  13. %code requires
  14. {
  15. #include <string>
  16. #include <eval/token.h>
  17. #include <eval/eval_context_decl.h>
  18. #include <dhcp/option.h>
  19. #include <boost/lexical_cast.hpp>
  20. using namespace isc::dhcp;
  21. using namespace isc::eval;
  22. }
  23. // The parsing context.
  24. %param { EvalContext& ctx }
  25. %locations
  26. %define parse.trace
  27. %define parse.error verbose
  28. %code
  29. {
  30. # include "eval_context.h"
  31. }
  32. %define api.token.prefix {TOKEN_}
  33. // Tokens in an order which makes sense and related to the intented use.
  34. %token
  35. END 0 "end of file"
  36. LPAREN "("
  37. RPAREN ")"
  38. NOT "not"
  39. AND "and"
  40. OR "or"
  41. EQUAL "=="
  42. OPTION "option"
  43. RELAY4 "relay4"
  44. RELAY6 "relay6"
  45. PEERADDR "peeraddr"
  46. LINKADDR "linkaddr"
  47. LBRACKET "["
  48. RBRACKET "]"
  49. DOT "."
  50. TEXT "text"
  51. HEX "hex"
  52. EXISTS "exists"
  53. PKT4 "pkt4"
  54. CHADDR "mac"
  55. HLEN "hlen"
  56. HTYPE "htype"
  57. CIADDR "ciaddr"
  58. GIADDR "giaddr"
  59. YIADDR "yiaddr"
  60. SIADDR "siaddr"
  61. SUBSTRING "substring"
  62. ALL "all"
  63. COMA ","
  64. CONCAT "concat"
  65. PKT6 "pkt6"
  66. MSGTYPE "msgtype"
  67. TRANSID "transid"
  68. VENDOR_CLASS "vendor-class"
  69. VENDOR "vendor"
  70. ANY "*"
  71. DATA "data"
  72. ENTERPRISE "enterprise"
  73. ;
  74. %token <std::string> STRING "constant string"
  75. %token <std::string> INTEGER "integer"
  76. %token <std::string> HEXSTRING "constant hexstring"
  77. %token <std::string> OPTION_NAME "option name"
  78. %token <std::string> IP_ADDRESS "ip address"
  79. %type <uint16_t> option_code
  80. %type <uint32_t> enterprise_id
  81. %type <TokenOption::RepresentationType> option_repr_type
  82. %type <TokenRelay6Field::FieldType> relay6_field
  83. %type <uint8_t> nest_level
  84. %type <TokenPkt4::FieldType> pkt4_field
  85. %type <TokenPkt6::FieldType> pkt6_field
  86. %left OR
  87. %left AND
  88. %precedence NOT
  89. %printer { yyoutput << $$; } <*>;
  90. %%
  91. // The whole grammar starts with an expression.
  92. %start expression;
  93. // Expression can either be a single token or a (something == something) expression
  94. expression : bool_expr
  95. ;
  96. bool_expr : "(" bool_expr ")"
  97. | NOT bool_expr
  98. {
  99. TokenPtr neg(new TokenNot());
  100. ctx.expression.push_back(neg);
  101. }
  102. | bool_expr AND bool_expr
  103. {
  104. TokenPtr neg(new TokenAnd());
  105. ctx.expression.push_back(neg);
  106. }
  107. | bool_expr OR bool_expr
  108. {
  109. TokenPtr neg(new TokenOr());
  110. ctx.expression.push_back(neg);
  111. }
  112. | string_expr EQUAL string_expr
  113. {
  114. TokenPtr eq(new TokenEqual());
  115. ctx.expression.push_back(eq);
  116. }
  117. | OPTION "[" option_code "]" "." EXISTS
  118. {
  119. TokenPtr opt(new TokenOption($3, TokenOption::EXISTS));
  120. ctx.expression.push_back(opt);
  121. }
  122. | RELAY4 "[" option_code "]" "." EXISTS
  123. {
  124. switch (ctx.getUniverse()) {
  125. case Option::V4:
  126. {
  127. TokenPtr opt(new TokenRelay4Option($3, TokenOption::EXISTS));
  128. ctx.expression.push_back(opt);
  129. break;
  130. }
  131. case Option::V6:
  132. // We will have relay6[123] for the DHCPv6.
  133. // In a very distant future we'll possibly be able
  134. // to mix both if we have DHCPv4-over-DHCPv6, so it
  135. // has some sense to make it explicit whether we
  136. // talk about DHCPv4 relay or DHCPv6 relay. However,
  137. // for the time being relay4 can be used in DHCPv4
  138. // only.
  139. error(@1, "relay4 can only be used in DHCPv4.");
  140. }
  141. }
  142. | RELAY6 "[" nest_level "]" "." OPTION "[" option_code "]" "." EXISTS
  143. {
  144. switch (ctx.getUniverse()) {
  145. case Option::V6:
  146. {
  147. TokenPtr opt(new TokenRelay6Option($3, $8, TokenOption::EXISTS));
  148. ctx.expression.push_back(opt);
  149. break;
  150. }
  151. case Option::V4:
  152. // For now we only use relay6 in DHCPv6.
  153. error(@1, "relay6 can only be used in DHCPv6.");
  154. }
  155. }
  156. | VENDOR_CLASS "[" enterprise_id "]" "." EXISTS
  157. {
  158. // Expression: vendor-class[1234].exists
  159. //
  160. // This token will find option 124 (DHCPv4) or 16 (DHCPv6), and will check
  161. // if enterprise-id equals specified value.
  162. TokenPtr exist(new TokenVendorClass(ctx.getUniverse(), $3, TokenOption::EXISTS));
  163. ctx.expression.push_back(exist);
  164. }
  165. | VENDOR "[" enterprise_id "]" "." EXISTS
  166. {
  167. // Expression: vendor[1234].exists
  168. //
  169. // This token will find option 125 (DHCPv4) or 17 (DHCPv6), and will check
  170. // if enterprise-id equals specified value.
  171. TokenPtr exist(new TokenVendor(ctx.getUniverse(), $3, TokenOption::EXISTS));
  172. ctx.expression.push_back(exist);
  173. }
  174. | VENDOR "[" enterprise_id "]" "." OPTION "[" option_code "]" "." EXISTS
  175. {
  176. // Expression vendor[1234].option[123].exists
  177. //
  178. // This token will check if specified vendor option exists, has specified
  179. // enterprise-id and if has specified suboption.
  180. TokenPtr exist(new TokenVendor(ctx.getUniverse(), $3, TokenOption::EXISTS, $8));
  181. ctx.expression.push_back(exist);
  182. }
  183. ;
  184. string_expr : STRING
  185. {
  186. TokenPtr str(new TokenString($1));
  187. ctx.expression.push_back(str);
  188. }
  189. | HEXSTRING
  190. {
  191. TokenPtr hex(new TokenHexString($1));
  192. ctx.expression.push_back(hex);
  193. }
  194. | IP_ADDRESS
  195. {
  196. TokenPtr ip(new TokenIpAddress($1));
  197. ctx.expression.push_back(ip);
  198. }
  199. | OPTION "[" option_code "]" "." option_repr_type
  200. {
  201. TokenPtr opt(new TokenOption($3, $6));
  202. ctx.expression.push_back(opt);
  203. }
  204. | RELAY4 "[" option_code "]" "." option_repr_type
  205. {
  206. switch (ctx.getUniverse()) {
  207. case Option::V4:
  208. {
  209. TokenPtr opt(new TokenRelay4Option($3, $6));
  210. ctx.expression.push_back(opt);
  211. break;
  212. }
  213. case Option::V6:
  214. // We will have relay6[123] for the DHCPv6.
  215. // In a very distant future we'll possibly be able
  216. // to mix both if we have DHCPv4-over-DHCPv6, so it
  217. // has some sense to make it explicit whether we
  218. // talk about DHCPv4 relay or DHCPv6 relay. However,
  219. // for the time being relay4 can be used in DHCPv4
  220. // only.
  221. error(@1, "relay4 can only be used in DHCPv4.");
  222. }
  223. }
  224. | RELAY6 "[" nest_level "]" "." OPTION "[" option_code "]" "." option_repr_type
  225. {
  226. switch (ctx.getUniverse()) {
  227. case Option::V6:
  228. {
  229. TokenPtr opt(new TokenRelay6Option($3, $8, $11));
  230. ctx.expression.push_back(opt);
  231. break;
  232. }
  233. case Option::V4:
  234. // For now we only use relay6 in DHCPv6.
  235. error(@1, "relay6 can only be used in DHCPv6.");
  236. }
  237. }
  238. | RELAY6 "[" nest_level "]" "." relay6_field
  239. {
  240. switch (ctx.getUniverse()) {
  241. case Option::V6:
  242. {
  243. TokenPtr relay6field(new TokenRelay6Field($3, $6));
  244. ctx.expression.push_back(relay6field);
  245. break;
  246. }
  247. case Option::V4:
  248. // For now we only use relay6 in DHCPv6.
  249. error(@1, "relay6 can only be used in DHCPv6.");
  250. }
  251. }
  252. | PKT4 "." pkt4_field
  253. {
  254. TokenPtr pkt4_field(new TokenPkt4($3));
  255. ctx.expression.push_back(pkt4_field);
  256. }
  257. | PKT6 "." pkt6_field
  258. {
  259. TokenPtr pkt6_field(new TokenPkt6($3));
  260. ctx.expression.push_back(pkt6_field);
  261. }
  262. | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
  263. {
  264. TokenPtr sub(new TokenSubstring());
  265. ctx.expression.push_back(sub);
  266. }
  267. | CONCAT "(" string_expr "," string_expr ")"
  268. {
  269. TokenPtr conc(new TokenConcat());
  270. ctx.expression.push_back(conc);
  271. }
  272. | VENDOR "." ENTERPRISE
  273. {
  274. // expression: vendor.enterprise
  275. //
  276. // This token will return enterprise-id number of received vendor option.
  277. TokenPtr vendor(new TokenVendor(ctx.getUniverse(), 0, TokenVendor::ENTERPRISE_ID));
  278. ctx.expression.push_back(vendor);
  279. }
  280. | VENDOR_CLASS "." ENTERPRISE
  281. {
  282. // expression: vendor-class.enterprise
  283. //
  284. // This token will return enterprise-id number of received vendor class option.
  285. TokenPtr vendor(new TokenVendorClass(ctx.getUniverse(), 0,
  286. TokenVendor::ENTERPRISE_ID));
  287. ctx.expression.push_back(vendor);
  288. }
  289. | VENDOR "[" enterprise_id "]" "." OPTION "[" option_code "]" "." option_repr_type
  290. {
  291. // expression: vendor[1234].option[56].exists
  292. // expression: vendor[1234].option[56].hex
  293. //
  294. // This token will search for vendor option with specified enterprise-id.
  295. // If found, will search for specified suboption and finally will return
  296. // if it exists ('exists') or its content ('hex')
  297. TokenPtr opt(new TokenVendor(ctx.getUniverse(), $3, $11, $8));
  298. ctx.expression.push_back(opt);
  299. }
  300. | VENDOR_CLASS "[" enterprise_id "]" "." DATA
  301. {
  302. // expression: vendor-class[1234].data
  303. //
  304. // Vendor class option does not have suboptions, but chunks of data (typically 1,
  305. // but the option structure allows multiple of them). If chunk offset is not
  306. // specified, we assume the first (0th) is requested.
  307. TokenPtr vendor_class(new TokenVendorClass(ctx.getUniverse(), $3,
  308. TokenVendor::DATA, 0));
  309. ctx.expression.push_back(vendor_class);
  310. }
  311. | VENDOR_CLASS "[" enterprise_id "]" "." DATA "[" INTEGER "]"
  312. {
  313. // expression: vendor-class[1234].data[5]
  314. //
  315. // Vendor class option does not have suboptions, but chunks of data (typically 1,
  316. // but the option structure allows multiple of them). This syntax specifies
  317. // which data chunk (tuple) we want.
  318. uint8_t index = ctx.convertUint8($8, @8);
  319. TokenPtr vendor_class(new TokenVendorClass(ctx.getUniverse(), $3,
  320. TokenVendor::DATA, index));
  321. ctx.expression.push_back(vendor_class);
  322. }
  323. ;
  324. option_code : INTEGER
  325. {
  326. $$ = ctx.convertOptionCode($1, @1);
  327. }
  328. | OPTION_NAME
  329. {
  330. $$ = ctx.convertOptionName($1, @1);
  331. }
  332. ;
  333. option_repr_type : TEXT
  334. {
  335. $$ = TokenOption::TEXTUAL;
  336. }
  337. | HEX
  338. {
  339. $$ = TokenOption::HEXADECIMAL;
  340. }
  341. ;
  342. enterprise_id : INTEGER
  343. {
  344. $$ = ctx.convertUint32($1, @1);
  345. }
  346. | "*"
  347. {
  348. $$ = 0;
  349. }
  350. pkt4_field : CHADDR
  351. {
  352. $$ = TokenPkt4::CHADDR;
  353. }
  354. | HLEN
  355. {
  356. $$ = TokenPkt4::HLEN;
  357. }
  358. | HTYPE
  359. {
  360. $$ = TokenPkt4::HTYPE;
  361. }
  362. | CIADDR
  363. {
  364. $$ = TokenPkt4::CIADDR;
  365. }
  366. | GIADDR
  367. {
  368. $$ = TokenPkt4::GIADDR;
  369. }
  370. | YIADDR
  371. {
  372. $$ = TokenPkt4::YIADDR;
  373. }
  374. | SIADDR
  375. {
  376. $$ = TokenPkt4::SIADDR;
  377. }
  378. ;
  379. start_expr : INTEGER
  380. {
  381. TokenPtr str(new TokenString($1));
  382. ctx.expression.push_back(str);
  383. }
  384. ;
  385. length_expr : INTEGER
  386. {
  387. TokenPtr str(new TokenString($1));
  388. ctx.expression.push_back(str);
  389. }
  390. | ALL
  391. {
  392. TokenPtr str(new TokenString("all"));
  393. ctx.expression.push_back(str);
  394. }
  395. ;
  396. relay6_field : PEERADDR { $$ = TokenRelay6Field::PEERADDR; }
  397. | LINKADDR { $$ = TokenRelay6Field::LINKADDR; }
  398. ;
  399. nest_level : INTEGER
  400. {
  401. $$ = ctx.convertNestLevelNumber($1, @1);
  402. }
  403. // Eventually we may add strings to handle different
  404. // ways of choosing from which relay we want to extract
  405. // an option or field.
  406. ;
  407. pkt6_field:MSGTYPE { $$ = TokenPkt6::MSGTYPE; }
  408. | TRANSID { $$ = TokenPkt6::TRANSID; }
  409. ;
  410. %%
  411. void
  412. isc::eval::EvalParser::error(const location_type& loc,
  413. const std::string& what)
  414. {
  415. ctx.error(loc, what);
  416. }