123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444 |
- /* Copyright (C) 2015-2016 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 {EvalParser}
- %define api.token.constructor
- %define api.value.type variant
- %define api.namespace {isc::eval}
- %define parse.assert
- %code requires
- {
- #include <string>
- #include <eval/token.h>
- #include <eval/eval_context_decl.h>
- #include <dhcp/option.h>
- #include <boost/lexical_cast.hpp>
- using namespace isc::dhcp;
- using namespace isc::eval;
- }
- // The parsing context.
- %param { EvalContext& ctx }
- %locations
- %define parse.trace
- %define parse.error verbose
- %code
- {
- # include "eval_context.h"
- }
- %define api.token.prefix {TOKEN_}
- // Tokens in an order which makes sense and related to the intented use.
- %token
- END 0 "end of file"
- LPAREN "("
- RPAREN ")"
- NOT "not"
- AND "and"
- OR "or"
- EQUAL "=="
- OPTION "option"
- RELAY4 "relay4"
- RELAY6 "relay6"
- PEERADDR "peeraddr"
- LINKADDR "linkaddr"
- LBRACKET "["
- RBRACKET "]"
- DOT "."
- TEXT "text"
- HEX "hex"
- EXISTS "exists"
- PKT4 "pkt4"
- CHADDR "mac"
- HLEN "hlen"
- HTYPE "htype"
- CIADDR "ciaddr"
- GIADDR "giaddr"
- YIADDR "yiaddr"
- SIADDR "siaddr"
- SUBSTRING "substring"
- ALL "all"
- COMA ","
- CONCAT "concat"
- PKT6 "pkt6"
- MSGTYPE "msgtype"
- TRANSID "transid"
- VENDOR_CLASS "vendor-class"
- VENDOR "vendor"
- ANY "*"
- DATA "data"
- ENTERPRISE "enterprise"
- ;
- %token <std::string> STRING "constant string"
- %token <std::string> INTEGER "integer"
- %token <std::string> HEXSTRING "constant hexstring"
- %token <std::string> OPTION_NAME "option name"
- %token <std::string> IP_ADDRESS "ip address"
- %type <uint16_t> option_code
- %type <uint32_t> enterprise_id
- %type <TokenOption::RepresentationType> option_repr_type
- %type <TokenRelay6Field::FieldType> relay6_field
- %type <uint8_t> nest_level
- %type <TokenPkt4::FieldType> pkt4_field
- %type <TokenPkt6::FieldType> pkt6_field
- %left OR
- %left AND
- %precedence NOT
- %printer { yyoutput << $$; } <*>;
- %%
- // The whole grammar starts with an expression.
- %start expression;
- // Expression can either be a single token or a (something == something) expression
- expression : bool_expr
- ;
- bool_expr : "(" bool_expr ")"
- | NOT bool_expr
- {
- TokenPtr neg(new TokenNot());
- ctx.expression.push_back(neg);
- }
- | bool_expr AND bool_expr
- {
- TokenPtr neg(new TokenAnd());
- ctx.expression.push_back(neg);
- }
- | bool_expr OR bool_expr
- {
- TokenPtr neg(new TokenOr());
- ctx.expression.push_back(neg);
- }
- | string_expr EQUAL string_expr
- {
- TokenPtr eq(new TokenEqual());
- ctx.expression.push_back(eq);
- }
- | OPTION "[" option_code "]" "." EXISTS
- {
- TokenPtr opt(new TokenOption($3, TokenOption::EXISTS));
- ctx.expression.push_back(opt);
- }
- | RELAY4 "[" option_code "]" "." EXISTS
- {
- switch (ctx.getUniverse()) {
- case Option::V4:
- {
- TokenPtr opt(new TokenRelay4Option($3, TokenOption::EXISTS));
- ctx.expression.push_back(opt);
- break;
- }
- case Option::V6:
- // We will have relay6[123] for the DHCPv6.
- // In a very distant future we'll possibly be able
- // to mix both if we have DHCPv4-over-DHCPv6, so it
- // has some sense to make it explicit whether we
- // talk about DHCPv4 relay or DHCPv6 relay. However,
- // for the time being relay4 can be used in DHCPv4
- // only.
- error(@1, "relay4 can only be used in DHCPv4.");
- }
- }
- | RELAY6 "[" nest_level "]" "." OPTION "[" option_code "]" "." EXISTS
- {
- switch (ctx.getUniverse()) {
- case Option::V6:
- {
- TokenPtr opt(new TokenRelay6Option($3, $8, TokenOption::EXISTS));
- ctx.expression.push_back(opt);
- break;
- }
- case Option::V4:
- // For now we only use relay6 in DHCPv6.
- error(@1, "relay6 can only be used in DHCPv6.");
- }
- }
- | VENDOR_CLASS "[" enterprise_id "]" "." EXISTS
- {
- // Expression: vendor-class[1234].exists
- //
- // This token will find option 124 (DHCPv4) or 16 (DHCPv6), and will check
- // if enterprise-id equals specified value.
- TokenPtr exist(new TokenVendorClass(ctx.getUniverse(), $3, TokenOption::EXISTS));
- ctx.expression.push_back(exist);
- }
- | VENDOR "[" enterprise_id "]" "." EXISTS
- {
- // Expression: vendor[1234].exists
- //
- // This token will find option 125 (DHCPv4) or 17 (DHCPv6), and will check
- // if enterprise-id equals specified value.
- TokenPtr exist(new TokenVendor(ctx.getUniverse(), $3, TokenOption::EXISTS));
- ctx.expression.push_back(exist);
- }
- | VENDOR "[" enterprise_id "]" "." OPTION "[" option_code "]" "." EXISTS
- {
- // Expression vendor[1234].option[123].exists
- //
- // This token will check if specified vendor option exists, has specified
- // enterprise-id and if has specified suboption.
- TokenPtr exist(new TokenVendor(ctx.getUniverse(), $3, TokenOption::EXISTS, $8));
- ctx.expression.push_back(exist);
- }
- ;
- string_expr : STRING
- {
- TokenPtr str(new TokenString($1));
- ctx.expression.push_back(str);
- }
- | HEXSTRING
- {
- TokenPtr hex(new TokenHexString($1));
- ctx.expression.push_back(hex);
- }
- | IP_ADDRESS
- {
- TokenPtr ip(new TokenIpAddress($1));
- ctx.expression.push_back(ip);
- }
- | OPTION "[" option_code "]" "." option_repr_type
- {
- TokenPtr opt(new TokenOption($3, $6));
- ctx.expression.push_back(opt);
- }
- | RELAY4 "[" option_code "]" "." option_repr_type
- {
- switch (ctx.getUniverse()) {
- case Option::V4:
- {
- TokenPtr opt(new TokenRelay4Option($3, $6));
- ctx.expression.push_back(opt);
- break;
- }
- case Option::V6:
- // We will have relay6[123] for the DHCPv6.
- // In a very distant future we'll possibly be able
- // to mix both if we have DHCPv4-over-DHCPv6, so it
- // has some sense to make it explicit whether we
- // talk about DHCPv4 relay or DHCPv6 relay. However,
- // for the time being relay4 can be used in DHCPv4
- // only.
- error(@1, "relay4 can only be used in DHCPv4.");
- }
- }
- | RELAY6 "[" nest_level "]" "." OPTION "[" option_code "]" "." option_repr_type
- {
- switch (ctx.getUniverse()) {
- case Option::V6:
- {
- TokenPtr opt(new TokenRelay6Option($3, $8, $11));
- ctx.expression.push_back(opt);
- break;
- }
- case Option::V4:
- // For now we only use relay6 in DHCPv6.
- error(@1, "relay6 can only be used in DHCPv6.");
- }
- }
- | RELAY6 "[" nest_level "]" "." relay6_field
- {
- switch (ctx.getUniverse()) {
- case Option::V6:
- {
- TokenPtr relay6field(new TokenRelay6Field($3, $6));
- ctx.expression.push_back(relay6field);
- break;
- }
- case Option::V4:
- // For now we only use relay6 in DHCPv6.
- error(@1, "relay6 can only be used in DHCPv6.");
- }
- }
- | PKT4 "." pkt4_field
- {
- TokenPtr pkt4_field(new TokenPkt4($3));
- ctx.expression.push_back(pkt4_field);
- }
- | PKT6 "." pkt6_field
- {
- TokenPtr pkt6_field(new TokenPkt6($3));
- ctx.expression.push_back(pkt6_field);
- }
- | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
- {
- TokenPtr sub(new TokenSubstring());
- ctx.expression.push_back(sub);
- }
- | CONCAT "(" string_expr "," string_expr ")"
- {
- TokenPtr conc(new TokenConcat());
- ctx.expression.push_back(conc);
- }
- | VENDOR "." ENTERPRISE
- {
- // expression: vendor.enterprise
- //
- // This token will return enterprise-id number of received vendor option.
- TokenPtr vendor(new TokenVendor(ctx.getUniverse(), 0, TokenVendor::ENTERPRISE_ID));
- ctx.expression.push_back(vendor);
- }
- | VENDOR_CLASS "." ENTERPRISE
- {
- // expression: vendor-class.enterprise
- //
- // This token will return enterprise-id number of received vendor class option.
- TokenPtr vendor(new TokenVendorClass(ctx.getUniverse(), 0,
- TokenVendor::ENTERPRISE_ID));
- ctx.expression.push_back(vendor);
- }
- | VENDOR "[" enterprise_id "]" "." OPTION "[" option_code "]" "." option_repr_type
- {
- // expression: vendor[1234].option[56].exists
- // expression: vendor[1234].option[56].hex
- //
- // This token will search for vendor option with specified enterprise-id.
- // If found, will search for specified suboption and finally will return
- // if it exists ('exists') or its content ('hex')
- TokenPtr opt(new TokenVendor(ctx.getUniverse(), $3, $11, $8));
- ctx.expression.push_back(opt);
- }
- | VENDOR_CLASS "[" enterprise_id "]" "." DATA
- {
- // expression: vendor-class[1234].data
- //
- // Vendor class option does not have suboptions, but chunks of data (typically 1,
- // but the option structure allows multiple of them). If chunk offset is not
- // specified, we assume the first (0th) is requested.
- TokenPtr vendor_class(new TokenVendorClass(ctx.getUniverse(), $3,
- TokenVendor::DATA, 0));
- ctx.expression.push_back(vendor_class);
- }
- | VENDOR_CLASS "[" enterprise_id "]" "." DATA "[" INTEGER "]"
- {
- // expression: vendor-class[1234].data[5]
- //
- // Vendor class option does not have suboptions, but chunks of data (typically 1,
- // but the option structure allows multiple of them). This syntax specifies
- // which data chunk (tuple) we want.
- uint8_t index = ctx.convertUint8($8, @8);
- TokenPtr vendor_class(new TokenVendorClass(ctx.getUniverse(), $3,
- TokenVendor::DATA, index));
- ctx.expression.push_back(vendor_class);
- }
- ;
- option_code : INTEGER
- {
- $$ = ctx.convertOptionCode($1, @1);
- }
- | OPTION_NAME
- {
- $$ = ctx.convertOptionName($1, @1);
- }
- ;
- option_repr_type : TEXT
- {
- $$ = TokenOption::TEXTUAL;
- }
- | HEX
- {
- $$ = TokenOption::HEXADECIMAL;
- }
- ;
- enterprise_id : INTEGER
- {
- $$ = ctx.convertUint32($1, @1);
- }
- | "*"
- {
- $$ = 0;
- }
- pkt4_field : CHADDR
- {
- $$ = TokenPkt4::CHADDR;
- }
- | HLEN
- {
- $$ = TokenPkt4::HLEN;
- }
- | HTYPE
- {
- $$ = TokenPkt4::HTYPE;
- }
- | CIADDR
- {
- $$ = TokenPkt4::CIADDR;
- }
- | GIADDR
- {
- $$ = TokenPkt4::GIADDR;
- }
- | YIADDR
- {
- $$ = TokenPkt4::YIADDR;
- }
- | SIADDR
- {
- $$ = TokenPkt4::SIADDR;
- }
- ;
- start_expr : INTEGER
- {
- TokenPtr str(new TokenString($1));
- ctx.expression.push_back(str);
- }
- ;
- length_expr : INTEGER
- {
- TokenPtr str(new TokenString($1));
- ctx.expression.push_back(str);
- }
- | ALL
- {
- TokenPtr str(new TokenString("all"));
- ctx.expression.push_back(str);
- }
- ;
- relay6_field : PEERADDR { $$ = TokenRelay6Field::PEERADDR; }
- | LINKADDR { $$ = TokenRelay6Field::LINKADDR; }
- ;
- nest_level : INTEGER
- {
- $$ = ctx.convertNestLevelNumber($1, @1);
- }
- // Eventually we may add strings to handle different
- // ways of choosing from which relay we want to extract
- // an option or field.
- ;
- pkt6_field:MSGTYPE { $$ = TokenPkt6::MSGTYPE; }
- | TRANSID { $$ = TokenPkt6::TRANSID; }
- ;
- %%
- void
- isc::eval::EvalParser::error(const location_type& loc,
- const std::string& what)
- {
- ctx.error(loc, what);
- }
|