parser.yy 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  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. PKT "pkt"
  54. IFACE "iface"
  55. SRC "src"
  56. DST "dst"
  57. LEN "len"
  58. PKT4 "pkt4"
  59. CHADDR "mac"
  60. HLEN "hlen"
  61. HTYPE "htype"
  62. CIADDR "ciaddr"
  63. GIADDR "giaddr"
  64. YIADDR "yiaddr"
  65. SIADDR "siaddr"
  66. SUBSTRING "substring"
  67. ALL "all"
  68. COMA ","
  69. CONCAT "concat"
  70. PKT6 "pkt6"
  71. MSGTYPE "msgtype"
  72. TRANSID "transid"
  73. VENDOR_CLASS "vendor-class"
  74. VENDOR "vendor"
  75. ANY "*"
  76. DATA "data"
  77. ENTERPRISE "enterprise"
  78. ;
  79. %token <std::string> STRING "constant string"
  80. %token <std::string> INTEGER "integer"
  81. %token <std::string> HEXSTRING "constant hexstring"
  82. %token <std::string> OPTION_NAME "option name"
  83. %token <std::string> IP_ADDRESS "ip address"
  84. %type <uint16_t> option_code
  85. %type <uint32_t> enterprise_id
  86. %type <uint32_t> integer_expr
  87. %type <TokenOption::RepresentationType> option_repr_type
  88. %type <TokenRelay6Field::FieldType> relay6_field
  89. %type <uint8_t> nest_level
  90. %type <TokenPkt::MetadataType> pkt_metadata
  91. %type <TokenPkt4::FieldType> pkt4_field
  92. %type <TokenPkt6::FieldType> pkt6_field
  93. %left OR
  94. %left AND
  95. %precedence NOT
  96. %printer { yyoutput << $$; } <*>;
  97. %%
  98. // The whole grammar starts with an expression.
  99. %start expression;
  100. // Expression can either be a single token or a (something == something) expression
  101. expression : bool_expr
  102. ;
  103. bool_expr : "(" bool_expr ")"
  104. | NOT bool_expr
  105. {
  106. TokenPtr neg(new TokenNot());
  107. ctx.expression.push_back(neg);
  108. }
  109. | bool_expr AND bool_expr
  110. {
  111. TokenPtr neg(new TokenAnd());
  112. ctx.expression.push_back(neg);
  113. }
  114. | bool_expr OR bool_expr
  115. {
  116. TokenPtr neg(new TokenOr());
  117. ctx.expression.push_back(neg);
  118. }
  119. | string_expr EQUAL string_expr
  120. {
  121. TokenPtr eq(new TokenEqual());
  122. ctx.expression.push_back(eq);
  123. }
  124. | OPTION "[" option_code "]" "." EXISTS
  125. {
  126. TokenPtr opt(new TokenOption($3, TokenOption::EXISTS));
  127. ctx.expression.push_back(opt);
  128. }
  129. | RELAY4 "[" option_code "]" "." EXISTS
  130. {
  131. switch (ctx.getUniverse()) {
  132. case Option::V4:
  133. {
  134. TokenPtr opt(new TokenRelay4Option($3, TokenOption::EXISTS));
  135. ctx.expression.push_back(opt);
  136. break;
  137. }
  138. case Option::V6:
  139. // We will have relay6[123] for the DHCPv6.
  140. // In a very distant future we'll possibly be able
  141. // to mix both if we have DHCPv4-over-DHCPv6, so it
  142. // has some sense to make it explicit whether we
  143. // talk about DHCPv4 relay or DHCPv6 relay. However,
  144. // for the time being relay4 can be used in DHCPv4
  145. // only.
  146. error(@1, "relay4 can only be used in DHCPv4.");
  147. }
  148. }
  149. | RELAY6 "[" nest_level "]" "." OPTION "[" option_code "]" "." EXISTS
  150. {
  151. switch (ctx.getUniverse()) {
  152. case Option::V6:
  153. {
  154. TokenPtr opt(new TokenRelay6Option($3, $8, TokenOption::EXISTS));
  155. ctx.expression.push_back(opt);
  156. break;
  157. }
  158. case Option::V4:
  159. // For now we only use relay6 in DHCPv6.
  160. error(@1, "relay6 can only be used in DHCPv6.");
  161. }
  162. }
  163. | VENDOR_CLASS "[" enterprise_id "]" "." EXISTS
  164. {
  165. // Expression: vendor-class[1234].exists
  166. //
  167. // This token will find option 124 (DHCPv4) or 16 (DHCPv6),
  168. // and will check if enterprise-id equals specified value.
  169. TokenPtr exist(new TokenVendorClass(ctx.getUniverse(), $3, TokenOption::EXISTS));
  170. ctx.expression.push_back(exist);
  171. }
  172. | VENDOR "[" enterprise_id "]" "." EXISTS
  173. {
  174. // Expression: vendor[1234].exists
  175. //
  176. // This token will find option 125 (DHCPv4) or 17 (DHCPv6),
  177. // and will check if enterprise-id equals specified value.
  178. TokenPtr exist(new TokenVendor(ctx.getUniverse(), $3, TokenOption::EXISTS));
  179. ctx.expression.push_back(exist);
  180. }
  181. | VENDOR "[" enterprise_id "]" "." OPTION "[" option_code "]" "." EXISTS
  182. {
  183. // Expression vendor[1234].option[123].exists
  184. //
  185. // This token will check if specified vendor option
  186. // exists, has specified enterprise-id and if has
  187. // specified suboption.
  188. TokenPtr exist(new TokenVendor(ctx.getUniverse(), $3, TokenOption::EXISTS, $8));
  189. ctx.expression.push_back(exist);
  190. }
  191. ;
  192. string_expr : STRING
  193. {
  194. TokenPtr str(new TokenString($1));
  195. ctx.expression.push_back(str);
  196. }
  197. | HEXSTRING
  198. {
  199. TokenPtr hex(new TokenHexString($1));
  200. ctx.expression.push_back(hex);
  201. }
  202. | IP_ADDRESS
  203. {
  204. TokenPtr ip(new TokenIpAddress($1));
  205. ctx.expression.push_back(ip);
  206. }
  207. | OPTION "[" option_code "]" "." option_repr_type
  208. {
  209. TokenPtr opt(new TokenOption($3, $6));
  210. ctx.expression.push_back(opt);
  211. }
  212. | RELAY4 "[" option_code "]" "." option_repr_type
  213. {
  214. switch (ctx.getUniverse()) {
  215. case Option::V4:
  216. {
  217. TokenPtr opt(new TokenRelay4Option($3, $6));
  218. ctx.expression.push_back(opt);
  219. break;
  220. }
  221. case Option::V6:
  222. // We will have relay6[123] for the DHCPv6.
  223. // In a very distant future we'll possibly be able
  224. // to mix both if we have DHCPv4-over-DHCPv6, so it
  225. // has some sense to make it explicit whether we
  226. // talk about DHCPv4 relay or DHCPv6 relay. However,
  227. // for the time being relay4 can be used in DHCPv4
  228. // only.
  229. error(@1, "relay4 can only be used in DHCPv4.");
  230. }
  231. }
  232. | RELAY6 "[" nest_level "]" "." OPTION "[" option_code "]" "." option_repr_type
  233. {
  234. switch (ctx.getUniverse()) {
  235. case Option::V6:
  236. {
  237. TokenPtr opt(new TokenRelay6Option($3, $8, $11));
  238. ctx.expression.push_back(opt);
  239. break;
  240. }
  241. case Option::V4:
  242. // For now we only use relay6 in DHCPv6.
  243. error(@1, "relay6 can only be used in DHCPv6.");
  244. }
  245. }
  246. | PKT "." pkt_metadata
  247. {
  248. TokenPtr pkt_metadata(new TokenPkt($3));
  249. ctx.expression.push_back(pkt_metadata);
  250. }
  251. | PKT4 "." pkt4_field
  252. {
  253. switch (ctx.getUniverse()) {
  254. case Option::V4:
  255. {
  256. TokenPtr pkt4_field(new TokenPkt4($3));
  257. ctx.expression.push_back(pkt4_field);
  258. break;
  259. }
  260. case Option::V6:
  261. // For now we only use pkt4 in DHCPv4.
  262. error(@1, "pkt4 can only be used in DHCPv4.");
  263. }
  264. }
  265. | PKT6 "." pkt6_field
  266. {
  267. switch (ctx.getUniverse()) {
  268. case Option::V6:
  269. {
  270. TokenPtr pkt6_field(new TokenPkt6($3));
  271. ctx.expression.push_back(pkt6_field);
  272. break;
  273. }
  274. case Option::V4:
  275. // For now we only use pkt6 in DHCPv6.
  276. error(@1, "pkt6 can only be used in DHCPv6.");
  277. }
  278. }
  279. | RELAY6 "[" nest_level "]" "." relay6_field
  280. {
  281. switch (ctx.getUniverse()) {
  282. case Option::V6:
  283. {
  284. TokenPtr relay6field(new TokenRelay6Field($3, $6));
  285. ctx.expression.push_back(relay6field);
  286. break;
  287. }
  288. case Option::V4:
  289. // For now we only use relay6 in DHCPv6.
  290. error(@1, "relay6 can only be used in DHCPv6.");
  291. }
  292. }
  293. | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
  294. {
  295. TokenPtr sub(new TokenSubstring());
  296. ctx.expression.push_back(sub);
  297. }
  298. | CONCAT "(" string_expr "," string_expr ")"
  299. {
  300. TokenPtr conc(new TokenConcat());
  301. ctx.expression.push_back(conc);
  302. }
  303. | VENDOR "." ENTERPRISE
  304. {
  305. // expression: vendor.enterprise
  306. //
  307. // This token will return enterprise-id number of
  308. // received vendor option.
  309. TokenPtr vendor(new TokenVendor(ctx.getUniverse(), 0, TokenVendor::ENTERPRISE_ID));
  310. ctx.expression.push_back(vendor);
  311. }
  312. | VENDOR_CLASS "." ENTERPRISE
  313. {
  314. // expression: vendor-class.enterprise
  315. //
  316. // This token will return enterprise-id number of
  317. // received vendor class option.
  318. TokenPtr vendor(new TokenVendorClass(ctx.getUniverse(), 0,
  319. TokenVendor::ENTERPRISE_ID));
  320. ctx.expression.push_back(vendor);
  321. }
  322. | VENDOR "[" enterprise_id "]" "." OPTION "[" option_code "]" "." option_repr_type
  323. {
  324. // This token will search for vendor option with
  325. // specified enterprise-id. If found, will search
  326. // for specified suboption and finally will return
  327. // its content.
  328. TokenPtr opt(new TokenVendor(ctx.getUniverse(), $3, $11, $8));
  329. ctx.expression.push_back(opt);
  330. }
  331. | VENDOR_CLASS "[" enterprise_id "]" "." DATA
  332. {
  333. // expression: vendor-class[1234].data
  334. //
  335. // Vendor class option does not have suboptions,
  336. // but chunks of data (typically 1, but the option
  337. // structure allows multiple of them). If chunk
  338. // offset is not specified, we assume the first (0th)
  339. // is requested.
  340. TokenPtr vendor_class(new TokenVendorClass(ctx.getUniverse(), $3,
  341. TokenVendor::DATA, 0));
  342. ctx.expression.push_back(vendor_class);
  343. }
  344. | VENDOR_CLASS "[" enterprise_id "]" "." DATA "[" INTEGER "]"
  345. {
  346. // expression: vendor-class[1234].data[5]
  347. //
  348. // Vendor class option does not have suboptions,
  349. // but chunks of data (typically 1, but the option
  350. // structure allows multiple of them). This syntax
  351. // specifies which data chunk (tuple) we want.
  352. uint8_t index = ctx.convertUint8($8, @8);
  353. TokenPtr vendor_class(new TokenVendorClass(ctx.getUniverse(), $3,
  354. TokenVendor::DATA, index));
  355. ctx.expression.push_back(vendor_class);
  356. }
  357. | integer_expr
  358. {
  359. TokenPtr integer(new TokenInteger($1));
  360. ctx.expression.push_back(integer);
  361. }
  362. ;
  363. integer_expr : INTEGER
  364. {
  365. $$ = ctx.convertUint32($1, @1);
  366. }
  367. ;
  368. option_code : INTEGER
  369. {
  370. $$ = ctx.convertOptionCode($1, @1);
  371. }
  372. | OPTION_NAME
  373. {
  374. $$ = ctx.convertOptionName($1, @1);
  375. }
  376. ;
  377. option_repr_type : TEXT
  378. {
  379. $$ = TokenOption::TEXTUAL;
  380. }
  381. | HEX
  382. {
  383. $$ = TokenOption::HEXADECIMAL;
  384. }
  385. ;
  386. nest_level : INTEGER
  387. {
  388. $$ = ctx.convertNestLevelNumber($1, @1);
  389. }
  390. // Eventually we may add strings to handle different
  391. // ways of choosing from which relay we want to extract
  392. // an option or field.
  393. ;
  394. pkt_metadata : IFACE
  395. {
  396. $$ = TokenPkt::IFACE;
  397. }
  398. | SRC
  399. {
  400. $$ = TokenPkt::SRC;
  401. }
  402. | DST
  403. {
  404. $$ = TokenPkt::DST;
  405. }
  406. | LEN
  407. {
  408. $$ = TokenPkt::LEN;
  409. }
  410. ;
  411. enterprise_id : INTEGER
  412. {
  413. $$ = ctx.convertUint32($1, @1);
  414. }
  415. | "*"
  416. {
  417. $$ = 0;
  418. }
  419. ;
  420. pkt4_field : CHADDR
  421. {
  422. $$ = TokenPkt4::CHADDR;
  423. }
  424. | HLEN
  425. {
  426. $$ = TokenPkt4::HLEN;
  427. }
  428. | HTYPE
  429. {
  430. $$ = TokenPkt4::HTYPE;
  431. }
  432. | CIADDR
  433. {
  434. $$ = TokenPkt4::CIADDR;
  435. }
  436. | GIADDR
  437. {
  438. $$ = TokenPkt4::GIADDR;
  439. }
  440. | YIADDR
  441. {
  442. $$ = TokenPkt4::YIADDR;
  443. }
  444. | SIADDR
  445. {
  446. $$ = TokenPkt4::SIADDR;
  447. }
  448. | MSGTYPE
  449. {
  450. $$ = TokenPkt4::MSGTYPE;
  451. }
  452. | TRANSID
  453. {
  454. $$ = TokenPkt4::TRANSID;
  455. }
  456. ;
  457. pkt6_field : MSGTYPE
  458. {
  459. $$ = TokenPkt6::MSGTYPE;
  460. }
  461. | TRANSID
  462. {
  463. $$ = TokenPkt6::TRANSID;
  464. }
  465. ;
  466. relay6_field : PEERADDR
  467. {
  468. $$ = TokenRelay6Field::PEERADDR;
  469. }
  470. | LINKADDR
  471. {
  472. $$ = TokenRelay6Field::LINKADDR;
  473. }
  474. ;
  475. start_expr : INTEGER
  476. {
  477. TokenPtr str(new TokenString($1));
  478. ctx.expression.push_back(str);
  479. }
  480. ;
  481. length_expr : INTEGER
  482. {
  483. TokenPtr str(new TokenString($1));
  484. ctx.expression.push_back(str);
  485. }
  486. | ALL
  487. {
  488. TokenPtr str(new TokenString("all"));
  489. ctx.expression.push_back(str);
  490. }
  491. ;
  492. %%
  493. void
  494. isc::eval::EvalParser::error(const location_type& loc,
  495. const std::string& what)
  496. {
  497. ctx.error(loc, what);
  498. }