Francis Dupont il y a 9 ans
Parent
commit
fd12a6d69f

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+1052.	[func]		marcin
+	libeval: expressions involving options can now use textual or
+	hexadecimal format of the options.
+	(Trac #4093 git 4cdf0fff1067b3dde6570dc6831e8b1343bc50fe)
+
 1051.	[func]		[tmark]
 	kea-dhcp4 and kea-dhcp6 configuration parsing now supports
 	the "client-classes" element for defining client classes.

+ 3 - 3
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -1696,7 +1696,7 @@ TEST_F(Dhcpv4SrvTest, matchClassification) {
         "    \"option-data\": ["
         "        {    \"name\": \"ip-forwarding\", "
         "             \"data\": \"true\" } ], "
-        "    \"test\": \"option[12] == 'foo'\" } ] }";
+        "    \"test\": \"option[12].text == 'foo'\" } ] }";
 
     ElementPtr json = Element::fromJSON(config);
     ConstElementPtr status;
@@ -1792,7 +1792,7 @@ TEST_F(Dhcpv4SrvTest, subnetClassPriority) {
         "    \"option-data\": ["
         "        {    \"name\": \"ip-forwarding\", "
         "             \"data\": \"true\" } ], "
-        "    \"test\": \"option[12] == 'foo'\" } ] }";
+        "    \"test\": \"option[12].text == 'foo'\" } ] }";
 
     ElementPtr json = Element::fromJSON(config);
     ConstElementPtr status;
@@ -1869,7 +1869,7 @@ TEST_F(Dhcpv4SrvTest, classGlobalPriority) {
         "    \"option-data\": ["
         "        {    \"name\": \"ip-forwarding\", "
         "             \"data\": \"true\" } ], "
-        "    \"test\": \"option[12] == 'foo'\" } ] }";
+        "    \"test\": \"option[12].text == 'foo'\" } ] }";
 
     ElementPtr json = Element::fromJSON(config);
     ConstElementPtr status;

+ 3 - 3
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -1866,7 +1866,7 @@ TEST_F(Dhcpv6SrvTest, matchClassification) {
         "    \"option-data\": ["
         "        {    \"name\": \"ipv6-forwarding\", "
         "             \"data\": \"true\" } ], "
-        "    \"test\": \"option[1234] == 'foo'\" } ] }";
+        "    \"test\": \"option[1234].text == 'foo'\" } ] }";
     ASSERT_NO_THROW(configure(config));
 
     // Create packets with enough to select the subnet
@@ -1962,7 +1962,7 @@ TEST_F(Dhcpv6SrvTest, subnetClassPriority) {
         "    \"option-data\": ["
         "        {    \"name\": \"ipv6-forwarding\", "
         "             \"data\": \"true\" } ], "
-        "    \"test\": \"option[1234] == 'foo'\" } ] }";
+        "    \"test\": \"option[1234].text == 'foo'\" } ] }";
     ASSERT_NO_THROW(configure(config));
 
     // Create a packet with enough to select the subnet and go through
@@ -2037,7 +2037,7 @@ TEST_F(Dhcpv6SrvTest, classGlobalPriority) {
         "    \"option-data\": ["
         "        {    \"name\": \"ipv6-forwarding\", "
         "             \"data\": \"true\" } ], "
-        "    \"test\": \"option[1234] == 'foo'\" } ] }";
+        "    \"test\": \"option[1234].text == 'foo'\" } ] }";
     ASSERT_NO_THROW(configure(config));
 
     // Create a packet with enough to select the subnet and go through

+ 29 - 0
src/lib/dhcp/option.cc

@@ -16,6 +16,7 @@
 #include <dhcp/libdhcp++.h>
 #include <dhcp/option.h>
 #include <exceptions/exceptions.h>
+#include <util/encode/hex.h>
 #include <util/io_utilities.h>
 
 #include <iomanip>
@@ -219,6 +220,34 @@ Option::toString() {
     return (toText(0));
 }
 
+std::string
+Option::toHexString(const bool include_header) {
+    OutputBuffer buf(len());
+    try {
+        // If the option is too long, exception will be thrown. We allow
+        // for this exception to propagate to not mask this error.
+        pack(buf);
+
+    } catch (const std::exception &ex) {
+        isc_throw(OutOfRange, "unable to obtain hexadecimal representation"
+                  " of option " << getType() << ": " << ex.what());
+    }
+    const uint8_t* option_data = static_cast<const uint8_t*>(buf.getData());
+    std::vector<uint8_t> option_vec;
+
+    // Assign option data to a vector, with or without option header depending
+    // on the value of "include_header" flag.
+    option_vec.assign(option_data + (include_header ? 0 : getHeaderLen()),
+                      option_data + buf.getLength());
+
+    // Return hexadecimal representation prepended with 0x or empty string
+    // if option has no payload and the header fields are excluded.
+    std::ostringstream s;
+    if (!option_vec.empty()) {
+        s << "0x" << encode::encodeHex(option_vec);
+    }
+    return (s.str());
+}
 
 std::string
 Option::headerToText(const int indent, const std::string& type_name) {

+ 9 - 0
src/lib/dhcp/option.h

@@ -216,6 +216,15 @@ public:
     /// @return string that represents the value of the option.
     virtual std::string toString();
 
+    /// @brief Returns string containing hexadecimal representation of option.
+    ///
+    /// @param include_header Boolean flag which indicates if the output should
+    /// also contain header fields. The default is that it shouldn't include
+    /// header fields.
+    ///
+    /// @return String containing hexadecimal representation of the option.
+    virtual std::string toHexString(const bool include_header = false);
+
     /// Returns option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
     ///
     /// @return option type

+ 48 - 0
src/lib/dhcp/tests/option_unittest.cc

@@ -236,6 +236,38 @@ TEST_F(OptionTest, v4_toText) {
     EXPECT_EQ("type=253, len=003: 00:0f:ff", opt.toText());
 }
 
+// Test converting option to the hexadecimal representation.
+TEST_F(OptionTest, v4_toHexString) {
+    std::vector<uint8_t> payload;
+    for (unsigned int i = 0; i < 16; ++i) {
+        payload.push_back(static_cast<uint8_t>(i));
+    }
+    Option opt(Option::V4, 122, payload);
+    EXPECT_EQ("0x000102030405060708090A0B0C0D0E0F", opt.toHexString());
+    EXPECT_EQ("0x7A10000102030405060708090A0B0C0D0E0F",
+              opt.toHexString(true));
+
+    // Test empty option.
+    Option opt_empty(Option::V4, 65, std::vector<uint8_t>());
+    EXPECT_TRUE(opt_empty.toHexString().empty());
+    EXPECT_EQ("0x4100", opt_empty.toHexString(true));
+
+    // Test too long option. We can't simply create such option by
+    // providing a long payload, because class constructor would not
+    // accept it. Instead we'll add two long sub options after we
+    // create an option instance.
+    Option opt_too_long(Option::V4, 33);
+    // Both suboptions have payloads of 150 bytes.
+    std::vector<uint8_t> long_payload(150, 1);
+    OptionPtr sub1(new Option(Option::V4, 100, long_payload));
+    OptionPtr sub2(new Option(Option::V4, 101, long_payload));
+    opt_too_long.addOption(sub1);
+    opt_too_long.addOption(sub2);
+
+    // The toHexString() should throw exception.
+    EXPECT_THROW(opt_too_long.toHexString(), isc::OutOfRange);
+}
+
 // Tests simple constructor
 TEST_F(OptionTest, v6_basic) {
 
@@ -446,6 +478,22 @@ TEST_F(OptionTest, v6_toText) {
     EXPECT_EQ("type=00258, len=00003: 00:0f:ff", opt->toText());
 }
 
+// Test converting option to the hexadecimal representation.
+TEST_F(OptionTest, v6_toHexString) {
+    std::vector<uint8_t> payload;
+    for (unsigned int i = 0; i < 16; ++i) {
+        payload.push_back(static_cast<uint8_t>(i));
+    }
+    Option opt(Option::V6, 12202, payload);
+    EXPECT_EQ("0x000102030405060708090A0B0C0D0E0F", opt.toHexString());
+    EXPECT_EQ("0x2FAA0010000102030405060708090A0B0C0D0E0F",
+              opt.toHexString(true));
+
+    // Test empty option.
+    Option opt_empty(Option::V6, 65000, std::vector<uint8_t>());
+    EXPECT_TRUE(opt_empty.toHexString().empty());
+    EXPECT_EQ("0xFDE80000", opt_empty.toHexString(true));
+}
 
 TEST_F(OptionTest, getUintX) {
 

+ 6 - 6
src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc

@@ -119,7 +119,7 @@ TEST(ExpressionParserTest, validExpression4) {
     ExpressionPtr parsed_expr;
 
     // Turn config into elements.  This may emit exceptions.
-    std::string cfg_txt = "\"option[100] == 'hundred4'\"";
+    std::string cfg_txt = "\"option[100].text == 'hundred4'\"";
     ElementPtr config_element = Element::fromJSON(cfg_txt);
 
     // Create the parser.
@@ -150,7 +150,7 @@ TEST(ExpressionParserTest, validExpression6) {
     ExpressionPtr parsed_expr;
 
     // Turn config into elements.  This may emit exceptions.
-    std::string cfg_txt = "\"option[100] == 'hundred6'\"";
+    std::string cfg_txt = "\"option[100].text == 'hundred6'\"";
     ElementPtr config_element = Element::fromJSON(cfg_txt);
 
     // Create the parser.
@@ -249,7 +249,7 @@ TEST_F(ClientClassDefParserTest, nameAndExpressionClass) {
     std::string cfg_text =
         "{ \n"
         "    \"name\": \"class_one\", \n"
-        "    \"test\": \"option[100] == 'works right'\" \n"
+        "    \"test\": \"option[100].text == 'works right'\" \n"
         "} \n";
 
     ClientClassDefPtr cclass;
@@ -325,7 +325,7 @@ TEST_F(ClientClassDefParserTest, basicValidClass) {
     std::string cfg_text =
         "{ \n"
         "    \"name\": \"MICROSOFT\", \n"
-        "    \"test\": \"option[100] == 'booya'\", \n"
+        "    \"test\": \"option[100].text == 'booya'\", \n"
         "    \"option-data\": [ \n"
         "        { \n"
         "           \"name\": \"domain-name-servers\", \n"
@@ -368,7 +368,7 @@ TEST_F(ClientClassDefParserTest, noClassName) {
 
     std::string cfg_text =
         "{ \n"
-        "    \"test\": \"option[123] == 'abc'\", \n"
+        "    \"test\": \"option[123].text == 'abc'\", \n"
         "    \"option-data\": [ \n"
         "        { \n"
         "           \"name\": \"domain-name-servers\", \n"
@@ -391,7 +391,7 @@ TEST_F(ClientClassDefParserTest, blankClassName) {
     std::string cfg_text =
         "{ \n"
         "    \"name\": \"\", \n"
-        "    \"test\": \"option[123] == 'abc'\", \n"
+        "    \"test\": \"option[123].text == 'abc'\", \n"
         "    \"option-data\": [ \n"
         "        { \n"
         "           \"name\": \"domain-name-servers\", \n"

+ 14 - 7
src/lib/eval/eval.dox

@@ -18,8 +18,9 @@
   @section dhcpEvalIntroduction Introduction
 
   The core of the libeval library is a parser that is able to parse an
-  expression (e.g. option[123] == 'APC'). This is currently used for client
-  classification, but in the future may be also used for other applications.
+  expression (e.g. option[123].text == 'APC'). This is currently used for
+  client classification, but in the future may be also used for other
+  applications.
 
   The external interface to the library is the @ref isc::eval::EvalContext
   class.  Once instantiated, it offers a major method:
@@ -79,12 +80,17 @@
 14.                 TokenPtr hex(new TokenHexString($1));
 15.                 ctx.expression.push_back(hex);
 16.             }
-17.       | OPTION '[' INTEGER ']'
+17.       | OPTION '[' INTEGER ']' DOT TEXT
 18.             {
-19.                 TokenPtr opt(new TokenOption($3));
+19.                 TokenPtr opt(new TokenOption($3, TokenOption::TEXTUAL));
 20.                 ctx.expression.push_back(opt);
-21.              }
-22.       ;
+21.             }
+22.       | OPTION '[' INTEGER ']' DOT HEX
+23.             {
+24.                 TokenPtr opt(new TokenOption($3, TokenOption::HEXADECIMAL));
+25.                 ctx.expression.push_back(opt);
+26.              }
+27.       ;
 @endcode
 
 This code determines that the grammar starts from expression (line 1).
@@ -92,7 +98,8 @@ The actual definition of expression (lines 3-5) may either be a
 single token or an expression "token == token" (EQUAL has been defined as
 "==" elsewhere). Token is further
 defined in lines 7-22: it may either be a string (lines 7-11),
-a hex string (lines 12-16) or option (lines 17-21).
+a hex string (lines 12-16), option in the textual format (lines 17-21)
+or option in a hexadecimal format (lines 22-26).
 When the actual case is determined, the respective C++ action
 is executed. For example, if the token is a string, the TokenString class is
 instantiated with the appropriate value and put onto the expression vector.

+ 136 - 111
src/lib/eval/lexer.cc

@@ -18,7 +18,7 @@
 #define FLEX_SCANNER
 #define YY_FLEX_MAJOR_VERSION 2
 #define YY_FLEX_MINOR_VERSION 5
-#define YY_FLEX_SUBMINOR_VERSION 39
+#define YY_FLEX_SUBMINOR_VERSION 35
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #define FLEX_BETA
 #endif
@@ -72,6 +72,7 @@ typedef int16_t flex_int16_t;
 typedef uint16_t flex_uint16_t;
 typedef int32_t flex_int32_t;
 typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
 #else
 typedef signed char flex_int8_t;
 typedef short int flex_int16_t;
@@ -79,6 +80,7 @@ typedef int flex_int32_t;
 typedef unsigned char flex_uint8_t; 
 typedef unsigned short int flex_uint16_t;
 typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
 
 /* Limits of integral types. */
 #ifndef INT8_MIN
@@ -109,8 +111,6 @@ typedef unsigned int flex_uint32_t;
 #define UINT32_MAX             (4294967295U)
 #endif
 
-#endif /* ! C99 */
-
 #endif /* ! FLEXINT_H */
 
 /* %endif */
@@ -225,18 +225,11 @@ extern FILE *yyin, *yyout;
      */
     #define  YY_LESS_LINENO(n) \
             do { \
-                int yyl;\
+                yy_size_t yyl;\
                 for ( yyl = n; yyl < yyleng; ++yyl )\
                     if ( yytext[yyl] == '\n' )\
                         --yylineno;\
             }while(0)
-    #define YY_LINENO_REWIND_TO(dst) \
-            do {\
-                const char *p;\
-                for ( p = yy_cp-1; p >= (dst); --p)\
-                    if ( *p == '\n' )\
-                        --yylineno;\
-            }while(0)
     
 /* Return all but the first "n" matched characters back to the input stream. */
 #define yyless(n) \
@@ -427,7 +420,7 @@ void yyfree (void *  );
 /* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */
 /* Begin user sect3 */
 
-#define yywrap() 1
+#define yywrap(n) 1
 #define YY_SKIP_YYWRAP
 
 #define FLEX_DEBUG
@@ -445,8 +438,6 @@ int yylineno = 1;
 extern char *yytext;
 #define yytext_ptr yytext
 
-/* %% [1.5] DFA */
-
 /* %if-c-only Standard (non-C++) definition */
 
 static yy_state_type yy_get_previous_state (void );
@@ -462,15 +453,15 @@ static void yy_fatal_error (yyconst char msg[]  );
 #define YY_DO_BEFORE_ACTION \
 	(yytext_ptr) = yy_bp; \
 /* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\
-	yyleng = (size_t) (yy_cp - yy_bp); \
+	yyleng = (yy_size_t) (yy_cp - yy_bp); \
 	(yy_hold_char) = *yy_cp; \
 	*yy_cp = '\0'; \
 /* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\
 	(yy_c_buf_p) = yy_cp;
 
 /* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */
-#define YY_NUM_RULES 16
-#define YY_END_OF_BUFFER 17
+#define YY_NUM_RULES 19
+#define YY_END_OF_BUFFER 20
 /* This struct is not used in this scanner,
    but its presence is necessary. */
 struct yy_trans_info
@@ -478,13 +469,14 @@ struct yy_trans_info
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static yyconst flex_int16_t yy_accept[44] =
+static yyconst flex_int16_t yy_accept[52] =
     {   0,
-        0,    0,   17,   15,    1,    2,   15,   10,   11,   14,
-       15,    5,    5,   15,   12,   13,   15,   15,   15,    1,
-        2,    0,    3,    5,    0,    6,    0,    0,    0,    4,
-        9,    0,    0,    0,    0,    0,    0,    7,    0,    0,
-        0,    8,    0
+        0,    0,   20,   18,    1,    2,   18,   13,   14,   17,
+       18,   12,    5,    5,   18,   15,   16,   18,   18,   18,
+       18,   18,    1,    2,    0,    3,    5,    0,    6,    0,
+        0,    0,    0,    0,    4,   11,    9,    0,    0,    0,
+        0,    0,    8,    0,    0,    7,    0,    0,    0,   10,
+        0
     } ;
 
 static yyconst flex_int32_t yy_ec[256] =
@@ -493,15 +485,15 @@ static yyconst flex_int32_t yy_ec[256] =
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    2,    1,    1,    1,    1,    1,    1,    4,    5,
-        6,    1,    1,    7,    8,    1,    1,    9,   10,   10,
-       10,   10,   10,   10,   10,   10,   10,    1,    1,    1,
-       11,    1,    1,    1,   12,   12,   12,   12,   12,   12,
+        6,    1,    1,    7,    8,    9,    1,   10,   11,   11,
+       11,   11,   11,   11,   11,   11,   11,    1,    1,    1,
+       12,    1,    1,    1,   13,   13,   13,   13,   13,   13,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    1,    1,    1,    1,    1,   13,    1,    1,
-       14,    1,   15,    1,    1,    1,   16,   17,   12,   12,
+        1,    1,    1,    1,    1,    1,    1,   14,    1,    1,
+       15,    1,   16,    1,    1,    1,   17,   18,   13,   13,
 
-       12,   12,   18,    1,   19,    1,    1,   20,    1,   21,
-       22,   23,    1,   24,   25,   26,   27,    1,    1,   13,
+       19,   13,   20,   21,   22,    1,    1,   23,    1,   24,
+       25,   26,    1,   27,   28,   29,   30,    1,    1,   31,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -519,61 +511,71 @@ static yyconst flex_int32_t yy_ec[256] =
         1,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int32_t yy_meta[28] =
+static yyconst flex_int32_t yy_meta[32] =
     {   0,
-        1,    1,    2,    1,    1,    1,    1,    1,    3,    3,
-        1,    3,    1,    1,    1,    3,    3,    1,    1,    1,
-        1,    1,    1,    1,    1,    1,    1
+        1,    1,    2,    1,    1,    1,    1,    1,    1,    3,
+        3,    1,    3,    1,    1,    1,    3,    3,    3,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1
     } ;
 
-static yyconst flex_int16_t yy_base[46] =
+static yyconst flex_int16_t yy_base[54] =
     {   0,
-        0,    0,   62,   63,   59,   57,   55,   63,   63,   63,
-       19,   21,   23,   47,   63,   63,   37,   33,   28,   52,
-       50,   48,   63,   26,    0,   63,   31,   24,   32,    0,
-       63,   29,   22,   24,   19,   23,   19,   63,   23,   20,
-       22,   63,   63,   36,   35
+        0,    0,   72,   73,   69,   67,   65,   73,   73,   73,
+       22,   73,   24,   26,   56,   73,   73,   44,   47,   39,
+       34,   44,   60,   58,   56,   73,   29,    0,   73,   36,
+       26,   25,   35,   21,    0,   73,   73,   29,   22,   20,
+       23,   18,   73,   22,   18,   73,   22,   19,   22,   73,
+       73,   55,   38
     } ;
 
-static yyconst flex_int16_t yy_def[46] =
+static yyconst flex_int16_t yy_def[54] =
     {   0,
-       43,    1,   43,   43,   43,   43,   44,   43,   43,   43,
-       43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
-       43,   44,   43,   43,   45,   43,   43,   43,   43,   45,
-       43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
-       43,   43,    0,   43,   43
+       51,    1,   51,   51,   51,   51,   52,   51,   51,   51,
+       51,   51,   51,   51,   51,   51,   51,   51,   51,   51,
+       51,   51,   51,   51,   52,   51,   51,   53,   51,   51,
+       51,   51,   51,   51,   53,   51,   51,   51,   51,   51,
+       51,   51,   51,   51,   51,   51,   51,   51,   51,   51,
+        0,   51,   51
     } ;
 
-static yyconst flex_int16_t yy_nxt[91] =
+static yyconst flex_int16_t yy_nxt[105] =
     {   0,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
-       14,    4,    4,   15,   16,   17,    4,    4,    4,    4,
-        4,   18,    4,    4,   19,    4,    4,   24,   24,   24,
-       24,   24,   24,   25,   24,   24,   22,   30,   22,   42,
-       41,   40,   39,   38,   37,   36,   35,   34,   33,   32,
-       31,   23,   21,   20,   29,   28,   27,   26,   23,   21,
-       20,   43,    3,   43,   43,   43,   43,   43,   43,   43,
-       43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
-       43,   43,   43,   43,   43,   43,   43,   43,   43,   43
+       14,   15,    4,    4,   16,   17,   18,    4,    4,    4,
+       19,    4,    4,    4,   20,    4,    4,   21,   22,    4,
+        4,   27,   27,   27,   27,   27,   27,   28,   27,   27,
+       35,   50,   49,   48,   47,   46,   45,   44,   43,   42,
+       41,   40,   39,   38,   28,   25,   37,   25,   36,   26,
+       24,   23,   34,   33,   32,   31,   30,   29,   26,   24,
+       23,   51,    3,   51,   51,   51,   51,   51,   51,   51,
+       51,   51,   51,   51,   51,   51,   51,   51,   51,   51,
+       51,   51,   51,   51,   51,   51,   51,   51,   51,   51,
+
+       51,   51,   51,   51
     } ;
 
-static yyconst flex_int16_t yy_chk[91] =
+static yyconst flex_int16_t yy_chk[105] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    1,    1,    1,    1,    1,   11,   11,   12,
-       12,   13,   13,   12,   24,   24,   44,   45,   44,   41,
-       40,   39,   37,   36,   35,   34,   33,   32,   29,   28,
-       27,   22,   21,   20,   19,   18,   17,   14,    7,    6,
-        5,    3,   43,   43,   43,   43,   43,   43,   43,   43,
-       43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
-       43,   43,   43,   43,   43,   43,   43,   43,   43,   43
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,   11,   11,   13,   13,   14,   14,   13,   27,   27,
+       53,   49,   48,   47,   45,   44,   42,   41,   40,   39,
+       38,   34,   33,   32,   13,   52,   31,   52,   30,   25,
+       24,   23,   22,   21,   20,   19,   18,   15,    7,    6,
+        5,    3,   51,   51,   51,   51,   51,   51,   51,   51,
+       51,   51,   51,   51,   51,   51,   51,   51,   51,   51,
+       51,   51,   51,   51,   51,   51,   51,   51,   51,   51,
+
+       51,   51,   51,   51
     } ;
 
 /* Table of booleans, true if rule could match eol. */
-static yyconst flex_int32_t yy_rule_can_match_eol[17] =
+static yyconst flex_int32_t yy_rule_can_match_eol[20] =
     {   0,
-0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,     };
+0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+        };
 
 static yy_state_type yy_last_accepting_state;
 static char *yy_last_accepting_cpos;
@@ -581,10 +583,10 @@ static char *yy_last_accepting_cpos;
 extern int yy_flex_debug;
 int yy_flex_debug = 1;
 
-static yyconst flex_int16_t yy_rule_linenum[16] =
+static yyconst flex_int16_t yy_rule_linenum[19] =
     {   0,
        83,   87,   93,  103,  109,  123,  124,  125,  126,  127,
-      128,  129,  130,  131,  133
+      128,  129,  130,  131,  132,  133,  134,  136
     } ;
 
 /* The intent behind this definition is that it'll catch
@@ -651,7 +653,7 @@ static isc::eval::location loc;
 // by moving it ahead by yyleng bytes. yyleng specifies the length of the
 // currently matched token.
 #define YY_USER_ACTION  loc.columns(yyleng);
-#line 655 "lexer.cc"
+#line 657 "lexer.cc"
 
 #define INITIAL 0
 
@@ -769,7 +771,7 @@ static int input (void );
 /* This used to be an fputs(), but since the string might contain NUL's,
  * we now use fwrite().
  */
-#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
 /* %endif */
 /* %if-c++-only C++ definition */
 /* %endif */
@@ -784,7 +786,7 @@ static int input (void );
 	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
 		{ \
 		int c = '*'; \
-		size_t n; \
+		yy_size_t n; \
 		for ( n = 0; n < max_size && \
 			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
 			buf[n] = (char) c; \
@@ -890,6 +892,17 @@ YY_DECL
 	register char *yy_cp, *yy_bp;
 	register int yy_act;
     
+/* %% [7.0] user's declarations go here */
+#line 76 "lexer.ll"
+
+
+
+    // Code run each time yylex is called.
+    loc.step();
+
+
+#line 905 "lexer.cc"
+
 	if ( !(yy_init) )
 		{
 		(yy_init) = 1;
@@ -924,18 +937,6 @@ YY_DECL
 		yy_load_buffer_state( );
 		}
 
-	{
-/* %% [7.0] user's declarations go here */
-#line 76 "lexer.ll"
-
-
-
-    // Code run each time yylex is called.
-    loc.step();
-
-
-#line 938 "lexer.cc"
-
 	while ( 1 )		/* loops until end-of-file is reached */
 		{
 /* %% [8.0] yymore()-related code goes here */
@@ -954,7 +955,7 @@ YY_DECL
 yy_match:
 		do
 			{
-			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
 			if ( yy_accept[yy_current_state] )
 				{
 				(yy_last_accepting_state) = yy_current_state;
@@ -963,13 +964,13 @@ yy_match:
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
-				if ( yy_current_state >= 44 )
+				if ( yy_current_state >= 52 )
 					yy_c = yy_meta[(unsigned int) yy_c];
 				}
 			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
 			++yy_cp;
 			}
-		while ( yy_current_state != 43 );
+		while ( yy_current_state != 51 );
 		yy_cp = (yy_last_accepting_cpos);
 		yy_current_state = (yy_last_accepting_state);
 
@@ -998,13 +999,13 @@ do_action:	/* This label is used only to access EOF actions. */
 			{
 			if ( yy_act == 0 )
 				fprintf( stderr, "--scanner backing up\n" );
-			else if ( yy_act < 16 )
+			else if ( yy_act < 19 )
 				fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
 				         (long)yy_rule_linenum[yy_act], yytext );
-			else if ( yy_act == 16 )
+			else if ( yy_act == 19 )
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				         yytext );
-			else if ( yy_act == 17 )
+			else if ( yy_act == 20 )
 				fprintf( stderr, "--(end of buffer or a NUL)\n" );
 			else
 				fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
@@ -1090,53 +1091,68 @@ return isc::eval::EvalParser::make_OPTION(loc);
 case 8:
 YY_RULE_SETUP
 #line 125 "lexer.ll"
-return isc::eval::EvalParser::make_SUBSTRING(loc);
+return isc::eval::EvalParser::make_TEXT(loc);
 	YY_BREAK
 case 9:
 YY_RULE_SETUP
 #line 126 "lexer.ll"
-return isc::eval::EvalParser::make_ALL(loc);
+return isc::eval::EvalParser::make_HEX(loc);
 	YY_BREAK
 case 10:
 YY_RULE_SETUP
 #line 127 "lexer.ll"
-return isc::eval::EvalParser::make_LPAREN(loc);
+return isc::eval::EvalParser::make_SUBSTRING(loc);
 	YY_BREAK
 case 11:
 YY_RULE_SETUP
 #line 128 "lexer.ll"
-return isc::eval::EvalParser::make_RPAREN(loc);
+return isc::eval::EvalParser::make_ALL(loc);
 	YY_BREAK
 case 12:
 YY_RULE_SETUP
 #line 129 "lexer.ll"
-return isc::eval::EvalParser::make_LBRACKET(loc);
+return isc::eval::EvalParser::make_DOT(loc);
 	YY_BREAK
 case 13:
 YY_RULE_SETUP
 #line 130 "lexer.ll"
-return isc::eval::EvalParser::make_RBRACKET(loc);
+return isc::eval::EvalParser::make_LPAREN(loc);
 	YY_BREAK
 case 14:
 YY_RULE_SETUP
 #line 131 "lexer.ll"
-return isc::eval::EvalParser::make_COMA(loc);
+return isc::eval::EvalParser::make_RPAREN(loc);
 	YY_BREAK
 case 15:
 YY_RULE_SETUP
+#line 132 "lexer.ll"
+return isc::eval::EvalParser::make_LBRACKET(loc);
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
 #line 133 "lexer.ll"
+return isc::eval::EvalParser::make_RBRACKET(loc);
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 134 "lexer.ll"
+return isc::eval::EvalParser::make_COMA(loc);
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 136 "lexer.ll"
 driver.error (loc, "Invalid character: " + std::string(yytext));
 	YY_BREAK
 case YY_STATE_EOF(INITIAL):
-#line 134 "lexer.ll"
+#line 137 "lexer.ll"
 return isc::eval::EvalParser::make_END(loc);
 	YY_BREAK
-case 16:
+case 19:
 YY_RULE_SETUP
-#line 135 "lexer.ll"
+#line 138 "lexer.ll"
 ECHO;
 	YY_BREAK
-#line 1140 "lexer.cc"
+#line 1156 "lexer.cc"
 
 	case YY_END_OF_BUFFER:
 		{
@@ -1267,7 +1283,6 @@ ECHO;
 			"fatal flex scanner internal error--no action found" );
 	} /* end of action switch */
 		} /* end of scanning one token */
-	} /* end of user's declarations */
 } /* end of yylex */
 /* %ok-for-header */
 
@@ -1342,7 +1357,7 @@ static int yy_get_next_buffer (void)
 			{ /* Not enough room in the buffer - grow it. */
 
 			/* just a shorter name for the current buffer */
-			YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
 
 			int yy_c_buf_p_offset =
 				(int) ((yy_c_buf_p) - b->yy_ch_buf);
@@ -1449,7 +1464,7 @@ static int yy_get_next_buffer (void)
 		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 			{
 			yy_current_state = (int) yy_def[yy_current_state];
-			if ( yy_current_state >= 44 )
+			if ( yy_current_state >= 52 )
 				yy_c = yy_meta[(unsigned int) yy_c];
 			}
 		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@@ -1482,13 +1497,13 @@ static int yy_get_next_buffer (void)
 	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 		{
 		yy_current_state = (int) yy_def[yy_current_state];
-		if ( yy_current_state >= 44 )
+		if ( yy_current_state >= 52 )
 			yy_c = yy_meta[(unsigned int) yy_c];
 		}
 	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-	yy_is_jam = (yy_current_state == 43);
+	yy_is_jam = (yy_current_state == 51);
 
-		return yy_is_jam ? 0 : yy_current_state;
+	return yy_is_jam ? 0 : yy_current_state;
 }
 
 /* %if-c-only */
@@ -1547,7 +1562,7 @@ static int yy_get_next_buffer (void)
 				case EOB_ACT_END_OF_FILE:
 					{
 					if ( yywrap( ) )
-						return EOF;
+						return 0;
 
 					if ( ! (yy_did_buffer_switch_on_eof) )
 						YY_NEW_FILE;
@@ -1711,6 +1726,17 @@ static void yy_load_buffer_state  (void)
 	yyfree((void *) b  );
 }
 
+/* %if-c-only */
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* %endif */
+
+/* %if-c++-only */
+/* %endif */
+
 /* Initializes or reinitializes a buffer.
  * This function is sometimes called more than once on the same buffer,
  * such as during a yyrestart() or at EOF.
@@ -1951,8 +1977,8 @@ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
 /* %if-c-only */
 /** Setup the input buffer state to scan the given bytes. The next call to yylex() will
  * scan from a @e copy of @a bytes.
- * @param yybytes the byte buffer to scan
- * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
  * 
  * @return the newly allocated buffer state object.
  */
@@ -1960,8 +1986,7 @@ YY_BUFFER_STATE yy_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len
 {
 	YY_BUFFER_STATE b;
 	char *buf;
-	yy_size_t n;
-	yy_size_t i;
+	yy_size_t n, i;
     
 	/* Get memory for full buffer, including space for trailing EOB's. */
 	n = _yybytes_len + 2;
@@ -2224,7 +2249,7 @@ void yyfree (void * ptr )
 
 /* %ok-for-header */
 
-#line 135 "lexer.ll"
+#line 138 "lexer.ll"
 
 
 

+ 3 - 0
src/lib/eval/lexer.ll

@@ -122,8 +122,11 @@ blank [ \t]
 
 "=="        return isc::eval::EvalParser::make_EQUAL(loc);
 "option"    return isc::eval::EvalParser::make_OPTION(loc);
+"text"      return isc::eval::EvalParser::make_TEXT(loc);
+"hex"       return isc::eval::EvalParser::make_HEX(loc);
 "substring" return isc::eval::EvalParser::make_SUBSTRING(loc);
 "all"       return isc::eval::EvalParser::make_ALL(loc);
+"."         return isc::eval::EvalParser::make_DOT(loc);
 "("         return isc::eval::EvalParser::make_LPAREN(loc);
 ")"         return isc::eval::EvalParser::make_RPAREN(loc);
 "["         return isc::eval::EvalParser::make_LBRACKET(loc);

+ 0 - 1
src/lib/eval/location.hh

@@ -1,4 +1,3 @@
-// Generated 2015115
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Locations for Bison parsers in C++

+ 115 - 89
src/lib/eval/parser.cc

@@ -52,8 +52,34 @@
 #line 39 "parser.yy" // lalr1.cc:413
 
 # include "eval_context.h"
+#line 67 "parser.yy" // lalr1.cc:413
+
+namespace {
+
+/* Convert option code specified as string to an 16 bit unsigned
+   representation. If the option code is not within the range of
+   0..65535 an error is reported. */
+uint16_t
+convert_option_code(const std::string& option_code,
+                    const isc::eval::EvalParser::location_type& loc,
+                    EvalContext& ctx) {
+    int n = 0;
+    try {
+        n  = boost::lexical_cast<int>(option_code);
+    } catch (const boost::bad_lexical_cast &) {
+        // This can't happen...
+        ctx.error(loc, "Option code has invalid value in " + option_code);
+    }
+    if (n < 0 || n > 65535) {
+        ctx.error(loc, "Option code has invalid value in "
+                      + option_code + ". Allowed range: 0..65535");
+    }
+    return (static_cast<uint16_t>(n));
+}
+}
+
 
-#line 57 "parser.cc" // lalr1.cc:413
+#line 83 "parser.cc" // lalr1.cc:413
 
 
 #ifndef YY_
@@ -139,7 +165,7 @@
 
 #line 21 "parser.yy" // lalr1.cc:479
 namespace isc { namespace eval {
-#line 143 "parser.cc" // lalr1.cc:479
+#line 169 "parser.cc" // lalr1.cc:479
 
   /* Return YYSTR after stripping away unnecessary quotes and
      backslashes, so that it's suitable for yyerror.  The heuristic is
@@ -251,10 +277,10 @@ namespace isc { namespace eval {
   {
       switch (that.type_get ())
     {
-      case 12: // "constant string"
-      case 13: // "integer"
-      case 14: // "constant hexstring"
-      case 15: // TOKEN
+      case 15: // "constant string"
+      case 16: // "integer"
+      case 17: // "constant hexstring"
+      case 18: // TOKEN
         value.move< std::string > (that.value);
         break;
 
@@ -273,10 +299,10 @@ namespace isc { namespace eval {
     state = that.state;
       switch (that.type_get ())
     {
-      case 12: // "constant string"
-      case 13: // "integer"
-      case 14: // "constant hexstring"
-      case 15: // TOKEN
+      case 15: // "constant string"
+      case 16: // "integer"
+      case 17: // "constant hexstring"
+      case 18: // TOKEN
         value.copy< std::string > (that.value);
         break;
 
@@ -316,32 +342,32 @@ namespace isc { namespace eval {
         << yysym.location << ": ";
     switch (yytype)
     {
-            case 12: // "constant string"
+            case 15: // "constant string"
 
-#line 61 "parser.yy" // lalr1.cc:636
+#line 64 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 324 "parser.cc" // lalr1.cc:636
+#line 350 "parser.cc" // lalr1.cc:636
         break;
 
-      case 13: // "integer"
+      case 16: // "integer"
 
-#line 61 "parser.yy" // lalr1.cc:636
+#line 64 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 331 "parser.cc" // lalr1.cc:636
+#line 357 "parser.cc" // lalr1.cc:636
         break;
 
-      case 14: // "constant hexstring"
+      case 17: // "constant hexstring"
 
-#line 61 "parser.yy" // lalr1.cc:636
+#line 64 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 338 "parser.cc" // lalr1.cc:636
+#line 364 "parser.cc" // lalr1.cc:636
         break;
 
-      case 15: // TOKEN
+      case 18: // TOKEN
 
-#line 61 "parser.yy" // lalr1.cc:636
+#line 64 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 345 "parser.cc" // lalr1.cc:636
+#line 371 "parser.cc" // lalr1.cc:636
         break;
 
 
@@ -541,10 +567,10 @@ namespace isc { namespace eval {
          when using variants.  */
         switch (yyr1_[yyn])
     {
-      case 12: // "constant string"
-      case 13: // "integer"
-      case 14: // "constant hexstring"
-      case 15: // TOKEN
+      case 15: // "constant string"
+      case 16: // "integer"
+      case 17: // "constant hexstring"
+      case 18: // TOKEN
         yylhs.value.build< std::string > ();
         break;
 
@@ -566,92 +592,90 @@ namespace isc { namespace eval {
           switch (yyn)
             {
   case 3:
-#line 73 "parser.yy" // lalr1.cc:859
+#line 105 "parser.yy" // lalr1.cc:859
     {
                     TokenPtr eq(new TokenEqual());
                     ctx.expression.push_back(eq);
                 }
-#line 575 "parser.cc" // lalr1.cc:859
+#line 601 "parser.cc" // lalr1.cc:859
     break;
 
   case 4:
-#line 80 "parser.yy" // lalr1.cc:859
+#line 112 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                   }
-#line 584 "parser.cc" // lalr1.cc:859
+#line 610 "parser.cc" // lalr1.cc:859
     break;
 
   case 5:
-#line 85 "parser.yy" // lalr1.cc:859
+#line 117 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(hex);
                   }
-#line 593 "parser.cc" // lalr1.cc:859
+#line 619 "parser.cc" // lalr1.cc:859
     break;
 
   case 6:
-#line 90 "parser.yy" // lalr1.cc:859
+#line 122 "parser.yy" // lalr1.cc:859
     {
-                      int n = 0;
-                      try {
-                          n  = boost::lexical_cast<int>(yystack_[1].value.as< std::string > ());
-                      } catch (const boost::bad_lexical_cast &) {
-                          // This can't happen...
-                          ctx.error(yystack_[1].location,
-                                    "Option code has invalid value in " + yystack_[1].value.as< std::string > ());
-                      }
-                      if (n < 0 || n > 65535) {
-                          ctx.error(yystack_[1].location,
-                                    "Option code has invalid value in "
-                                    + yystack_[1].value.as< std::string > () + ". Allowed range: 0..65535");
-                      }
-                      TokenPtr opt(new TokenOption(static_cast<uint16_t>(n)));
+                      uint16_t numeric_code = convert_option_code(yystack_[3].value.as< std::string > (), yystack_[3].location, ctx);
+                      TokenPtr opt(new TokenOption(numeric_code, TokenOption::TEXTUAL));
                       ctx.expression.push_back(opt);
                   }
-#line 615 "parser.cc" // lalr1.cc:859
+#line 629 "parser.cc" // lalr1.cc:859
     break;
 
   case 7:
-#line 108 "parser.yy" // lalr1.cc:859
+#line 128 "parser.yy" // lalr1.cc:859
+    {
+                      uint16_t numeric_code = convert_option_code(yystack_[3].value.as< std::string > (), yystack_[3].location, ctx);
+                      TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL));
+                      ctx.expression.push_back(opt);
+                  }
+#line 639 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 8:
+#line 134 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr sub(new TokenSubstring());
                       ctx.expression.push_back(sub);
                   }
-#line 624 "parser.cc" // lalr1.cc:859
+#line 648 "parser.cc" // lalr1.cc:859
     break;
 
-  case 9:
-#line 117 "parser.yy" // lalr1.cc:859
+  case 10:
+#line 143 "parser.yy" // lalr1.cc:859
     {
                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                      ctx.expression.push_back(str);
                  }
-#line 633 "parser.cc" // lalr1.cc:859
+#line 657 "parser.cc" // lalr1.cc:859
     break;
 
-  case 10:
-#line 124 "parser.yy" // lalr1.cc:859
+  case 11:
+#line 150 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                   }
-#line 642 "parser.cc" // lalr1.cc:859
+#line 666 "parser.cc" // lalr1.cc:859
     break;
 
-  case 11:
-#line 129 "parser.yy" // lalr1.cc:859
+  case 12:
+#line 155 "parser.yy" // lalr1.cc:859
     {
                      TokenPtr str(new TokenString("all"));
                      ctx.expression.push_back(str);
                  }
-#line 651 "parser.cc" // lalr1.cc:859
+#line 675 "parser.cc" // lalr1.cc:859
     break;
 
 
-#line 655 "parser.cc" // lalr1.cc:859
+#line 679 "parser.cc" // lalr1.cc:859
             default:
               break;
             }
@@ -906,72 +930,74 @@ namespace isc { namespace eval {
   }
 
 
-  const signed char EvalParser::yypact_ninf_ = -9;
+  const signed char EvalParser::yypact_ninf_ = -10;
 
   const signed char EvalParser::yytable_ninf_ = -1;
 
   const signed char
   EvalParser::yypact_[] =
   {
-      -4,    -7,    -2,    -9,    -9,    -9,     7,    -9,     6,     0,
-      -4,    -9,    -4,     3,     8,    -9,    -9,     4,    -9,     9,
-      -1,    -9,    -9,    10,    -9
+      -4,    -9,    -3,   -10,   -10,   -10,     9,   -10,    12,     1,
+      -4,   -10,    -4,    -2,     6,   -10,    10,     2,     0,   -10,
+      11,   -10,   -10,    -6,   -10,   -10,     8,   -10
   };
 
   const unsigned char
   EvalParser::yydefact_[] =
   {
-       0,     0,     0,     4,     5,     8,     0,     2,     0,     0,
-       0,     1,     0,     0,     0,     3,     6,     0,     9,     0,
-       0,    11,    10,     0,     7
+       0,     0,     0,     4,     5,     9,     0,     2,     0,     0,
+       0,     1,     0,     0,     0,     3,     0,     0,     0,    10,
+       0,     6,     7,     0,    12,    11,     0,     8
   };
 
   const signed char
   EvalParser::yypgoto_[] =
   {
-      -9,    -9,    -9,    -8,    -9,    -9
+     -10,   -10,   -10,    -7,   -10,   -10
   };
 
   const signed char
   EvalParser::yydefgoto_[] =
   {
-      -1,     6,     7,     8,    19,    23
+      -1,     6,     7,     8,    20,    26
   };
 
   const unsigned char
   EvalParser::yytable_[] =
   {
-       1,     2,    14,     9,    15,    21,    10,    11,     3,    12,
-       4,     5,    22,    13,    16,    17,    20,    18,     0,    24
+       1,     2,    24,    14,     9,    15,    21,    22,    10,    11,
+      25,     3,    16,     4,     5,    12,    17,    13,    19,    18,
+      27,    23
   };
 
-  const signed char
+  const unsigned char
   EvalParser::yycheck_[] =
   {
-       4,     5,    10,    10,    12,     6,     8,     0,    12,     3,
-      14,    15,    13,    13,    11,     7,     7,    13,    -1,     9
+       4,     5,     8,    10,    13,    12,     6,     7,    11,     0,
+      16,    15,    14,    17,    18,     3,    10,    16,    16,     9,
+      12,    10
   };
 
   const unsigned char
   EvalParser::yystos_[] =
   {
-       0,     4,     5,    12,    14,    15,    17,    18,    19,    10,
-       8,     0,     3,    13,    19,    19,    11,     7,    13,    20,
-       7,     6,    13,    21,     9
+       0,     4,     5,    15,    17,    18,    20,    21,    22,    13,
+      11,     0,     3,    16,    22,    22,    14,    10,     9,    16,
+      23,     6,     7,    10,     8,    16,    24,    12
   };
 
   const unsigned char
   EvalParser::yyr1_[] =
   {
-       0,    16,    17,    18,    19,    19,    19,    19,    19,    20,
-      21,    21
+       0,    19,    20,    21,    22,    22,    22,    22,    22,    22,
+      23,    24,    24
   };
 
   const unsigned char
   EvalParser::yyr2_[] =
   {
-       0,     2,     1,     3,     1,     1,     4,     8,     1,     1,
-       1,     1
+       0,     2,     1,     3,     1,     1,     6,     6,     8,     1,
+       1,     1,     1
   };
 
 
@@ -982,18 +1008,18 @@ namespace isc { namespace eval {
   const EvalParser::yytname_[] =
   {
   "\"end of file\"", "error", "$undefined", "\"==\"", "\"option\"",
-  "\"substring\"", "\"all\"", "\",\"", "\"(\"", "\")\"", "\"[\"", "\"]\"",
-  "\"constant string\"", "\"integer\"", "\"constant hexstring\"", "TOKEN",
-  "$accept", "expression", "bool_expr", "string_expr", "start_expr",
-  "length_expr", YY_NULLPTR
+  "\"substring\"", "\"text\"", "\"hex\"", "\"all\"", "\".\"", "\",\"",
+  "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"", "\"integer\"",
+  "\"constant hexstring\"", "TOKEN", "$accept", "expression", "bool_expr",
+  "string_expr", "start_expr", "length_expr", YY_NULLPTR
   };
 
 #if YYDEBUG
   const unsigned char
   EvalParser::yyrline_[] =
   {
-       0,    69,    69,    72,    79,    84,    89,   107,   112,   116,
-     123,   128
+       0,   101,   101,   104,   111,   116,   121,   127,   133,   138,
+     142,   149,   154
   };
 
   // Print the state stack on the debug stream.
@@ -1028,8 +1054,8 @@ namespace isc { namespace eval {
 
 #line 21 "parser.yy" // lalr1.cc:1167
 } } // isc::eval
-#line 1032 "parser.cc" // lalr1.cc:1167
-#line 135 "parser.yy" // lalr1.cc:1168
+#line 1058 "parser.cc" // lalr1.cc:1167
+#line 161 "parser.yy" // lalr1.cc:1168
 
 void
 isc::eval::EvalParser::error(const location_type& loc,

+ 66 - 33
src/lib/eval/parser.h

@@ -325,16 +325,19 @@ namespace isc { namespace eval {
         TOKEN_EQUAL = 258,
         TOKEN_OPTION = 259,
         TOKEN_SUBSTRING = 260,
-        TOKEN_ALL = 261,
-        TOKEN_COMA = 262,
-        TOKEN_LPAREN = 263,
-        TOKEN_RPAREN = 264,
-        TOKEN_LBRACKET = 265,
-        TOKEN_RBRACKET = 266,
-        TOKEN_STRING = 267,
-        TOKEN_INTEGER = 268,
-        TOKEN_HEXSTRING = 269,
-        TOKEN_TOKEN = 270
+        TOKEN_TEXT = 261,
+        TOKEN_HEX = 262,
+        TOKEN_ALL = 263,
+        TOKEN_DOT = 264,
+        TOKEN_COMA = 265,
+        TOKEN_LPAREN = 266,
+        TOKEN_RPAREN = 267,
+        TOKEN_LBRACKET = 268,
+        TOKEN_RBRACKET = 269,
+        TOKEN_STRING = 270,
+        TOKEN_INTEGER = 271,
+        TOKEN_HEXSTRING = 272,
+        TOKEN_TOKEN = 273
       };
     };
 
@@ -459,10 +462,22 @@ namespace isc { namespace eval {
 
     static inline
     symbol_type
+    make_TEXT (const location_type& l);
+
+    static inline
+    symbol_type
+    make_HEX (const location_type& l);
+
+    static inline
+    symbol_type
     make_ALL (const location_type& l);
 
     static inline
     symbol_type
+    make_DOT (const location_type& l);
+
+    static inline
+    symbol_type
     make_COMA (const location_type& l);
 
     static inline
@@ -582,7 +597,7 @@ namespace isc { namespace eval {
   // number is the opposite.  If YYTABLE_NINF, syntax error.
   static const unsigned char yytable_[];
 
-  static const signed char yycheck_[];
+  static const unsigned char yycheck_[];
 
   // YYSTOS[STATE-NUM] -- The (internal number of the) accessing
   // symbol of state STATE-NUM.
@@ -702,12 +717,12 @@ namespace isc { namespace eval {
     enum
     {
       yyeof_ = 0,
-      yylast_ = 19,     ///< Last index in yytable_.
+      yylast_ = 21,     ///< Last index in yytable_.
       yynnts_ = 6,  ///< Number of nonterminal symbols.
       yyfinal_ = 11, ///< Termination state number.
       yyterror_ = 1,
       yyerrcode_ = 256,
-      yyntokens_ = 16  ///< Number of tokens.
+      yyntokens_ = 19  ///< Number of tokens.
     };
 
 
@@ -751,9 +766,9 @@ namespace isc { namespace eval {
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
        5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
-      15
+      15,    16,    17,    18
     };
-    const unsigned int user_token_number_max_ = 270;
+    const unsigned int user_token_number_max_ = 273;
     const token_number_type undef_token_ = 2;
 
     if (static_cast<int>(t) <= yyeof_)
@@ -786,10 +801,10 @@ namespace isc { namespace eval {
   {
       switch (other.type_get ())
     {
-      case 12: // "constant string"
-      case 13: // "integer"
-      case 14: // "constant hexstring"
-      case 15: // TOKEN
+      case 15: // "constant string"
+      case 16: // "integer"
+      case 17: // "constant hexstring"
+      case 18: // TOKEN
         value.copy< std::string > (other.value);
         break;
 
@@ -810,10 +825,10 @@ namespace isc { namespace eval {
     (void) v;
       switch (this->type_get ())
     {
-      case 12: // "constant string"
-      case 13: // "integer"
-      case 14: // "constant hexstring"
-      case 15: // TOKEN
+      case 15: // "constant string"
+      case 16: // "integer"
+      case 17: // "constant hexstring"
+      case 18: // TOKEN
         value.copy< std::string > (v);
         break;
 
@@ -865,10 +880,10 @@ namespace isc { namespace eval {
     // Type destructor.
     switch (yytype)
     {
-      case 12: // "constant string"
-      case 13: // "integer"
-      case 14: // "constant hexstring"
-      case 15: // TOKEN
+      case 15: // "constant string"
+      case 16: // "integer"
+      case 17: // "constant hexstring"
+      case 18: // TOKEN
         value.template destroy< std::string > ();
         break;
 
@@ -895,10 +910,10 @@ namespace isc { namespace eval {
     super_type::move(s);
       switch (this->type_get ())
     {
-      case 12: // "constant string"
-      case 13: // "integer"
-      case 14: // "constant hexstring"
-      case 15: // TOKEN
+      case 15: // "constant string"
+      case 16: // "integer"
+      case 17: // "constant hexstring"
+      case 18: // TOKEN
         value.move< std::string > (s.value);
         break;
 
@@ -958,7 +973,7 @@ namespace isc { namespace eval {
     yytoken_number_[] =
     {
        0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
-     265,   266,   267,   268,   269,   270
+     265,   266,   267,   268,   269,   270,   271,   272,   273
     };
     return static_cast<token_type> (yytoken_number_[type]);
   }
@@ -988,12 +1003,30 @@ namespace isc { namespace eval {
   }
 
   EvalParser::symbol_type
+  EvalParser::make_TEXT (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_TEXT, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_HEX (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_HEX, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_ALL (const location_type& l)
   {
     return symbol_type (token::TOKEN_ALL, l);
   }
 
   EvalParser::symbol_type
+  EvalParser::make_DOT (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_DOT, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_COMA (const location_type& l)
   {
     return symbol_type (token::TOKEN_COMA, l);
@@ -1050,7 +1083,7 @@ namespace isc { namespace eval {
 
 #line 21 "parser.yy" // lalr1.cc:392
 } } // isc::eval
-#line 1054 "parser.h" // lalr1.cc:392
+#line 1087 "parser.h" // lalr1.cc:392
 
 
 

+ 41 - 15
src/lib/eval/parser.yy

@@ -45,7 +45,10 @@ using namespace isc::eval;
   EQUAL "=="
   OPTION "option"
   SUBSTRING "substring"
+  TEXT "text"
+  HEX "hex"
   ALL "all"
+  DOT "."
   COMA ","
   LPAREN  "("
   RPAREN  ")"
@@ -59,6 +62,35 @@ using namespace isc::eval;
 %token <std::string> TOKEN
 
 %printer { yyoutput << $$; } <*>;
+
+%code
+{
+namespace {
+
+/* Convert option code specified as string to an 16 bit unsigned
+   representation. If the option code is not within the range of
+   0..65535 an error is reported. */
+uint16_t
+convert_option_code(const std::string& option_code,
+                    const isc::eval::EvalParser::location_type& loc,
+                    EvalContext& ctx) {
+    int n = 0;
+    try {
+        n  = boost::lexical_cast<int>(option_code);
+    } catch (const boost::bad_lexical_cast &) {
+        // This can't happen...
+        ctx.error(loc, "Option code has invalid value in " + option_code);
+    }
+    if (n < 0 || n > 65535) {
+        ctx.error(loc, "Option code has invalid value in "
+                      + option_code + ". Allowed range: 0..65535");
+    }
+    return (static_cast<uint16_t>(n));
+}
+}
+
+}
+
 %%
 
 // The whole grammar starts with an expression.
@@ -86,22 +118,16 @@ string_expr : STRING
                       TokenPtr hex(new TokenHexString($1));
                       ctx.expression.push_back(hex);
                   }
-            | OPTION "[" INTEGER "]"
+            | OPTION "[" INTEGER "]" DOT TEXT
+                  {
+                      uint16_t numeric_code = convert_option_code($3, @3, ctx);
+                      TokenPtr opt(new TokenOption(numeric_code, TokenOption::TEXTUAL));
+                      ctx.expression.push_back(opt);
+                  }
+            | OPTION "[" INTEGER "]" DOT HEX
                   {
-                      int n = 0;
-                      try {
-                          n  = boost::lexical_cast<int>($3);
-                      } catch (const boost::bad_lexical_cast &) {
-                          // This can't happen...
-                          ctx.error(@3,
-                                    "Option code has invalid value in " + $3);
-                      }
-                      if (n < 0 || n > 65535) {
-                          ctx.error(@3,
-                                    "Option code has invalid value in "
-                                    + $3 + ". Allowed range: 0..65535");
-                      }
-                      TokenPtr opt(new TokenOption(static_cast<uint16_t>(n)));
+                      uint16_t numeric_code = convert_option_code($3, @3, ctx);
+                      TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL));
                       ctx.expression.push_back(opt);
                   }
             | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"

+ 0 - 1
src/lib/eval/position.hh

@@ -1,4 +1,3 @@
-// Generated 2015115
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Positions for Bison parsers in C++

+ 0 - 1
src/lib/eval/stack.hh

@@ -1,4 +1,3 @@
-// Generated 2015115
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Stack handling for Bison parsers in C++

+ 23 - 9
src/lib/eval/tests/context_unittest.cc

@@ -117,7 +117,7 @@ TEST_F(EvalContextTest, basic) {
 
     EvalContext tmp;
 
-    EXPECT_NO_THROW(parsed_ = tmp.parseString("option[123] == 'MSFT'"));
+    EXPECT_NO_THROW(parsed_ = tmp.parseString("option[123].text == 'MSFT'"));
     EXPECT_TRUE(parsed_);
 }
 
@@ -143,7 +143,7 @@ TEST_F(EvalContextTest, integer) {
     EvalContext eval;
 
     EXPECT_NO_THROW(parsed_ =
-        eval.parseString("substring(option[123], 0, 2) == '42'"));
+        eval.parseString("substring(option[123].text, 0, 2) == '42'"));
     EXPECT_TRUE(parsed_);
 }
 
@@ -198,7 +198,17 @@ TEST_F(EvalContextTest, equal) {
 TEST_F(EvalContextTest, option) {
     EvalContext eval;
 
-    EXPECT_NO_THROW(parsed_ = eval.parseString("option[123] == 'foo'"));
+    EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'foo'"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(3, eval.expression.size());
+    checkTokenOption(eval.expression.at(0), 123);
+}
+
+// Test parsing of an option represented as hexadecimal string.
+TEST_F(EvalContextTest, optionHex) {
+    EvalContext eval;
+
+    EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].hex == 0x666F6F"));
     EXPECT_TRUE(parsed_);
     ASSERT_EQ(3, eval.expression.size());
     checkTokenOption(eval.expression.at(0), 123);
@@ -245,16 +255,20 @@ TEST_F(EvalContextTest, scanParseErrors) {
     checkError("0abc",
                "<string>:1.1: syntax error, unexpected integer");
     checkError("===", "<string>:1.1-2: syntax error, unexpected ==");
-    checkError("option[-1]",
+    checkError("option[-1].text",
                "<string>:1.8-9: Option code has invalid "
                "value in -1. Allowed range: 0..65535");
-    checkError("option[65536]",
+    checkError("option[65536].text",
                "<string>:1.8-12: Option code has invalid "
                "value in 65536. Allowed range: 0..65535");
-    checkError("option[12345678901234567890]",
+    checkError("option[12345678901234567890].text",
                "<string>:1.8-27: Failed to convert 12345678901234567890 "
                "to an integer.");
-    checkError("option[123] < 'foo'", "<string>:1.13: Invalid character: <");
+    checkError("option[123]",
+               "<string>:1.12: syntax error, unexpected end of file,"
+               " expecting .");
+    checkError("option[123].text < 'foo'", "<string>:1.18: Invalid"
+               " character: <");
     checkError("substring('foo',12345678901234567890,1)",
                "<string>:1.17-36: Failed to convert 12345678901234567890 "
                "to an integer.");
@@ -274,11 +288,11 @@ TEST_F(EvalContextTest, parseErrors) {
     checkError("option(10) == 'ab'",
                "<string>:1.7: syntax error, "
                "unexpected (, expecting [");
-    checkError("option['ab'] == 'foo'",
+    checkError("option['ab'].text == 'foo'",
                "<string>:1.8-11: syntax error, "
                "unexpected constant string, "
                "expecting integer");
-    checkError("option[0xa] == 'ab'",
+    checkError("option[0xa].text == 'ab'",
                "<string>:1.8-10: syntax error, "
                "unexpected constant hexstring, "
                "expecting integer");

+ 21 - 2
src/lib/eval/tests/evaluate_unittest.cc

@@ -196,7 +196,7 @@ TEST_F(EvaluateTest, packet) {
     TokenPtr tstring;
     TokenPtr tequal;
 
-    ASSERT_NO_THROW(toption.reset(new TokenOption(100)));
+    ASSERT_NO_THROW(toption.reset(new TokenOption(100, TokenOption::TEXTUAL)));
     e_.push_back(toption);
     ASSERT_NO_THROW(tstring.reset(new TokenString("hundred4")));
     e_.push_back(tstring);
@@ -209,6 +209,25 @@ TEST_F(EvaluateTest, packet) {
     EXPECT_FALSE(result_);
 }
 
+// A test which compares option value represented in hexadecimal format.
+TEST_F(EvaluateTest, optionHex) {
+    TokenPtr toption;
+    TokenPtr tstring;
+    TokenPtr tequal;
+
+    ASSERT_NO_THROW(toption.reset(new TokenOption(100, TokenOption::HEXADECIMAL)));
+    e_.push_back(toption);
+    ASSERT_NO_THROW(tstring.reset(new TokenString("0x68756E6472656434")));
+    e_.push_back(tstring);
+    ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
+    e_.push_back(tequal);
+
+    ASSERT_NO_THROW(result_ = evaluate(e_, *pkt4_));
+    EXPECT_TRUE(result_);
+    ASSERT_NO_THROW(result_ = evaluate(e_, *pkt6_));
+    EXPECT_FALSE(result_);
+}
+
 // A test using substring on an option.
 TEST_F(EvaluateTest, complex) {
     TokenPtr toption;
@@ -219,7 +238,7 @@ TEST_F(EvaluateTest, complex) {
     TokenPtr tequal;
 
     // Get the option, i.e., "hundred[46]"
-    ASSERT_NO_THROW(toption.reset(new TokenOption(100)));
+    ASSERT_NO_THROW(toption.reset(new TokenOption(100, TokenOption::TEXTUAL)));
     e_.push_back(toption);
 
     // Get substring("hundred[46]", 0, 7), i.e., "hundred"

+ 62 - 4
src/lib/eval/tests/token_unittest.cc

@@ -253,8 +253,8 @@ TEST_F(TokenTest, optionString4) {
     TokenPtr not_found;
 
     // The packets we use have option 100 with a string in them.
-    ASSERT_NO_THROW(found.reset(new TokenOption(100)));
-    ASSERT_NO_THROW(not_found.reset(new TokenOption(101)));
+    ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::TEXTUAL)));
+    ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::TEXTUAL)));
 
     // This should evaluate to the content of the option 100 (i.e. "hundred4")
     ASSERT_NO_THROW(found->evaluate(*pkt4_, values_));
@@ -274,6 +274,35 @@ TEST_F(TokenTest, optionString4) {
     EXPECT_EQ("hundred4", values_.top());
 }
 
+// This test checks if a token representing option value is able to extract
+// the option from an IPv4 packet and properly store its value in a
+// hexadecimal format.
+TEST_F(TokenTest, optionHexString4) {
+    TokenPtr found;
+    TokenPtr not_found;
+
+    // The packets we use have option 100 with a string in them.
+    ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::HEXADECIMAL)));
+    ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::HEXADECIMAL)));
+
+    // This should evaluate to the content of the option 100 (i.e. "hundred4")
+    ASSERT_NO_THROW(found->evaluate(*pkt4_, values_));
+
+    // This should evaluate to "" as there is no option 101.
+    ASSERT_NO_THROW(not_found->evaluate(*pkt4_, values_));
+
+    // There should be 2 values evaluated.
+    ASSERT_EQ(2, values_.size());
+
+    // This is a stack, so the pop order is inversed. We should get the empty
+    // string first.
+    EXPECT_EQ("", values_.top());
+    values_.pop();
+
+    // Then the content of the option 100.
+    EXPECT_EQ("0x68756E6472656434", values_.top());
+}
+
 // This test checks if a token representing an option value is able to extract
 // the option from an IPv6 packet and properly store the option's value.
 TEST_F(TokenTest, optionString6) {
@@ -281,8 +310,8 @@ TEST_F(TokenTest, optionString6) {
     TokenPtr not_found;
 
     // The packets we use have option 100 with a string in them.
-    ASSERT_NO_THROW(found.reset(new TokenOption(100)));
-    ASSERT_NO_THROW(not_found.reset(new TokenOption(101)));
+    ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::TEXTUAL)));
+    ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::TEXTUAL)));
 
     // This should evaluate to the content of the option 100 (i.e. "hundred6")
     ASSERT_NO_THROW(found->evaluate(*pkt6_, values_));
@@ -302,6 +331,35 @@ TEST_F(TokenTest, optionString6) {
     EXPECT_EQ("hundred6", values_.top());
 }
 
+// This test checks if a token representing an option value is able to extract
+// the option from an IPv6 packet and properly store its value in hexadecimal
+// format.
+TEST_F(TokenTest, optionHexString6) {
+    TokenPtr found;
+    TokenPtr not_found;
+
+    // The packets we use have option 100 with a string in them.
+    ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::HEXADECIMAL)));
+    ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::HEXADECIMAL)));
+
+    // This should evaluate to the content of the option 100 (i.e. "hundred6")
+    ASSERT_NO_THROW(found->evaluate(*pkt6_, values_));
+
+    // This should evaluate to "" as there is no option 101.
+    ASSERT_NO_THROW(not_found->evaluate(*pkt6_, values_));
+
+    // There should be 2 values evaluated.
+    ASSERT_EQ(2, values_.size());
+
+    // This is a stack, so the pop order is inversed. We should get the empty
+    // string first.
+    EXPECT_EQ("", values_.top());
+    values_.pop();
+
+    // Then the content of the option 100.
+    EXPECT_EQ("0x68756E6472656436", values_.top());
+}
+
 // This test checks if a token representing an == operator is able to
 // compare two values (with incorrectly built stack).
 TEST_F(TokenTest, optionEqualInvalid) {

+ 2 - 1
src/lib/eval/token.cc

@@ -66,7 +66,8 @@ void
 TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
     OptionPtr opt = pkt.getOption(option_code_);
     if (opt) {
-        values.push(opt->toString());
+        values.push(representation_type_ == TEXTUAL ? opt->toString()
+                    : opt->toHexString());
     } else {
         // Option not found, push empty string
         values.push("");

+ 18 - 3
src/lib/eval/token.h

@@ -146,6 +146,19 @@ protected:
 /// option. If the option is not found, an empty string ("") is returned.
 class TokenOption : public Token {
 public:
+
+    /// @brief Token representation type.
+    ///
+    /// There are many possible ways in which option can be presented.
+    /// Currently the textual and hexadecimal representations are
+    /// supported. The type of representation is specified in the
+    /// constructor and it affects the value generated by the
+    /// @c TokenOption::evaluate function.
+    enum RepresentationType {
+        TEXTUAL,
+        HEXADECIMAL
+    };
+
     /// @brief Constructor that takes an option code as a parameter
     /// @param option_code code of the option
     ///
@@ -153,8 +166,9 @@ public:
     /// introduce complex dependency of the libkea-eval on libdhcpsrv.
     ///
     /// @param option_code code of the option to be represented.
-    TokenOption(uint16_t option_code)
-        :option_code_(option_code) {}
+    /// @param rep_type Token representation type.
+    TokenOption(const uint16_t option_code, const RepresentationType& rep_type)
+        : option_code_(option_code), representation_type_(rep_type) {}
 
     /// @brief Evaluates the values of the option
     ///
@@ -177,7 +191,8 @@ public:
     }
 
 private:
-    uint16_t option_code_; ///< code of the option to be extracted
+    uint16_t option_code_; ///< Code of the option to be extracted
+    RepresentationType representation_type_; ///< Representation type.
 };
 
 /// @brief Token that represents equality operator (compares two other tokens)