Browse Source

[4204] Eval parser allows for referencing option by its name.

Marcin Siodelski 9 years ago
parent
commit
77e78881aa

+ 0 - 1
src/lib/dhcp/libdhcp++.cc

@@ -248,7 +248,6 @@ LibDHCP::clearRuntimeOptionDefs() {
     runtime_option_defs_.clearItems();
 }
 
-
 bool
 LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
     if (u == Option::V6) {

+ 0 - 1
src/lib/dhcp/libdhcp++.h

@@ -121,7 +121,6 @@ public:
     static OptionDefContainerPtr
     getRuntimeOptionDefs(const std::string& space);
 
-
     /// @brief Check if the specified option is a standard option.
     ///
     /// @param u universe (V4 or V6)

+ 1 - 1
src/lib/dhcpsrv/parsers/client_class_def_parser.cc

@@ -50,7 +50,7 @@ ExpressionParser::build(ConstElementPtr expression_cfg) {
     std::string value;
     expression_cfg->getValue(value);
     try {
-        EvalContext eval_ctx;
+        EvalContext eval_ctx(global_context_->universe_);
         eval_ctx.parseString(value);
         local_expression_.reset(new Expression());
         *local_expression_ = eval_ctx.expression;

+ 5 - 3
src/lib/eval/eval_context.cc

@@ -15,10 +15,12 @@
 #include <eval/eval_context.h>
 #include <eval/parser.h>
 #include <exceptions/exceptions.h>
+#include <dhcp/option.h>
 #include <fstream>
 
-EvalContext::EvalContext()
-  : trace_scanning_(false), trace_parsing_(false)
+EvalContext::EvalContext(const Option::Universe& option_universe)
+  : trace_scanning_(false), trace_parsing_(false),
+    option_universe_(option_universe)
 {
 }
 
@@ -32,7 +34,7 @@ EvalContext::parseString(const std::string& str)
     file_ = "<string>";
     string_ = str;
     scanStringBegin();
-    isc::eval::EvalParser parser(*this);
+    isc::eval::EvalParser parser(*this, option_universe_);
     parser.set_debug_level(trace_parsing_);
     int res = parser.parse();
     scanStringEnd();

+ 12 - 3
src/lib/eval/eval_context.h

@@ -42,7 +42,11 @@ class EvalContext
 {
 public:
     /// @brief Default constructor.
-    EvalContext();
+    ///
+    /// @param option_universe Option universe: DHCPv4 or DHCPv6. This is used
+    /// by the parser to determine which option definitions set should be used
+    /// to map option names to option codes.
+    EvalContext(const Option::Universe& option_universe);
 
     /// @brief destructor
     virtual ~EvalContext();
@@ -55,7 +59,7 @@ public:
 
     /// @brief Method called after the last tokens are scanned from a string.
     void scanStringEnd();
-    
+
     /// @brief Run the parser on the string specified.
     ///
     /// @param str string to be written
@@ -87,7 +91,12 @@ public:
 
     /// @brief Flag determing parser debugging.
     bool trace_parsing_;
-  
+
+    /// @brief Option universe: DHCPv4 or DHCPv6.
+    ///
+    /// This is used by the parser to determine which option definitions
+    /// set should be used to map option name to option code.
+    Option::Universe option_universe_;
 };
 
 }; // end of isc::eval namespace

+ 95 - 57
src/lib/eval/parser.cc

@@ -49,10 +49,10 @@
 
 #line 51 "parser.cc" // lalr1.cc:412
 // Unqualified %code blocks.
-#line 39 "parser.yy" // lalr1.cc:413
+#line 40 "parser.yy" // lalr1.cc:413
 
 # include "eval_context.h"
-#line 67 "parser.yy" // lalr1.cc:413
+#line 72 "parser.yy" // lalr1.cc:413
 
 namespace {
 
@@ -206,13 +206,14 @@ namespace isc { namespace eval {
 
 
   /// Build a parser object.
-  EvalParser::EvalParser (EvalContext& ctx_yyarg)
+  EvalParser::EvalParser (EvalContext& ctx_yyarg, const Option::Universe& option_universe_yyarg)
     :
 #if YYDEBUG
       yydebug_ (false),
       yycdebug_ (&std::cerr),
 #endif
-      ctx (ctx_yyarg)
+      ctx (ctx_yyarg),
+      option_universe (option_universe_yyarg)
   {}
 
   EvalParser::~EvalParser ()
@@ -344,30 +345,30 @@ namespace isc { namespace eval {
     {
             case 15: // "constant string"
 
-#line 64 "parser.yy" // lalr1.cc:636
+#line 69 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 350 "parser.cc" // lalr1.cc:636
+#line 351 "parser.cc" // lalr1.cc:636
         break;
 
       case 16: // "integer"
 
-#line 64 "parser.yy" // lalr1.cc:636
+#line 69 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 357 "parser.cc" // lalr1.cc:636
+#line 358 "parser.cc" // lalr1.cc:636
         break;
 
       case 17: // "constant hexstring"
 
-#line 64 "parser.yy" // lalr1.cc:636
+#line 69 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 364 "parser.cc" // lalr1.cc:636
+#line 365 "parser.cc" // lalr1.cc:636
         break;
 
       case 18: // TOKEN
 
-#line 64 "parser.yy" // lalr1.cc:636
+#line 69 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 371 "parser.cc" // lalr1.cc:636
+#line 372 "parser.cc" // lalr1.cc:636
         break;
 
 
@@ -592,90 +593,124 @@ namespace isc { namespace eval {
           switch (yyn)
             {
   case 3:
-#line 105 "parser.yy" // lalr1.cc:859
+#line 110 "parser.yy" // lalr1.cc:859
     {
                     TokenPtr eq(new TokenEqual());
                     ctx.expression.push_back(eq);
                 }
-#line 601 "parser.cc" // lalr1.cc:859
+#line 602 "parser.cc" // lalr1.cc:859
     break;
 
   case 4:
-#line 112 "parser.yy" // lalr1.cc:859
+#line 117 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                   }
-#line 610 "parser.cc" // lalr1.cc:859
+#line 611 "parser.cc" // lalr1.cc:859
     break;
 
   case 5:
-#line 117 "parser.yy" // lalr1.cc:859
+#line 122 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(hex);
                   }
-#line 619 "parser.cc" // lalr1.cc:859
+#line 620 "parser.cc" // lalr1.cc:859
     break;
 
   case 6:
-#line 122 "parser.yy" // lalr1.cc:859
+#line 127 "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::TEXTUAL));
                       ctx.expression.push_back(opt);
                   }
-#line 629 "parser.cc" // lalr1.cc:859
+#line 630 "parser.cc" // lalr1.cc:859
     break;
 
   case 7:
-#line 128 "parser.yy" // lalr1.cc:859
+#line 133 "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
+#line 640 "parser.cc" // lalr1.cc:859
     break;
 
   case 8:
-#line 134 "parser.yy" // lalr1.cc:859
+#line 139 "parser.yy" // lalr1.cc:859
+    {
+                      try {
+                          // This may result in exception if the specified
+                          // name is unknown.
+                          TokenPtr opt(new TokenOption(yystack_[3].value.as< std::string > (), option_universe,
+                                                       TokenOption::TEXTUAL));
+                          ctx.expression.push_back(opt);
+
+                      } catch (const std::exception& ex) {
+                          ctx.error(yystack_[3].location, ex.what());
+                      }
+                  }
+#line 657 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 9:
+#line 152 "parser.yy" // lalr1.cc:859
+    {
+                      try {
+                          // This may result in exception if the specified
+                          // name is unknown.
+                          TokenPtr opt(new TokenOption(yystack_[3].value.as< std::string > (), option_universe,
+                                                       TokenOption::HEXADECIMAL));
+                          ctx.expression.push_back(opt);
+
+                      } catch (const std::exception& ex) {
+                          ctx.error(yystack_[3].location, ex.what());
+                      }
+                  }
+#line 674 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 10:
+#line 165 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr sub(new TokenSubstring());
                       ctx.expression.push_back(sub);
                   }
-#line 648 "parser.cc" // lalr1.cc:859
+#line 683 "parser.cc" // lalr1.cc:859
     break;
 
-  case 10:
-#line 143 "parser.yy" // lalr1.cc:859
+  case 12:
+#line 174 "parser.yy" // lalr1.cc:859
     {
                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                      ctx.expression.push_back(str);
                  }
-#line 657 "parser.cc" // lalr1.cc:859
+#line 692 "parser.cc" // lalr1.cc:859
     break;
 
-  case 11:
-#line 150 "parser.yy" // lalr1.cc:859
+  case 13:
+#line 181 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                   }
-#line 666 "parser.cc" // lalr1.cc:859
+#line 701 "parser.cc" // lalr1.cc:859
     break;
 
-  case 12:
-#line 155 "parser.yy" // lalr1.cc:859
+  case 14:
+#line 186 "parser.yy" // lalr1.cc:859
     {
                      TokenPtr str(new TokenString("all"));
                      ctx.expression.push_back(str);
                  }
-#line 675 "parser.cc" // lalr1.cc:859
+#line 710 "parser.cc" // lalr1.cc:859
     break;
 
 
-#line 679 "parser.cc" // lalr1.cc:859
+#line 714 "parser.cc" // lalr1.cc:859
             default:
               break;
             }
@@ -937,17 +972,19 @@ namespace isc { namespace eval {
   const signed char
   EvalParser::yypact_[] =
   {
-      -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
+      -4,    -1,     6,   -10,   -10,   -10,     4,   -10,    15,    -9,
+      -4,   -10,    -4,     5,     7,    10,   -10,    13,    14,     8,
+       2,     9,   -10,    16,   -10,   -10,   -10,   -10,    -6,   -10,
+     -10,    17,   -10
   };
 
   const unsigned char
   EvalParser::yydefact_[] =
   {
-       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
+       0,     0,     0,     4,     5,    11,     0,     2,     0,     0,
+       0,     1,     0,     0,     0,     0,     3,     0,     0,     0,
+       0,     0,    12,     0,     8,     9,     6,     7,     0,    14,
+      13,     0,    10
   };
 
   const signed char
@@ -959,45 +996,46 @@ namespace isc { namespace eval {
   const signed char
   EvalParser::yydefgoto_[] =
   {
-      -1,     6,     7,     8,    20,    26
+      -1,     6,     7,     8,    23,    31
   };
 
   const unsigned char
   EvalParser::yytable_[] =
   {
-       1,     2,    24,    14,     9,    15,    21,    22,    10,    11,
-      25,     3,    16,     4,     5,    12,    17,    13,    19,    18,
-      27,    23
+       1,     2,    29,    15,    11,    16,    13,    14,    24,    25,
+      30,     3,     9,     4,     5,    26,    27,    10,    12,    17,
+      19,    18,    20,    21,    22,     0,    28,     0,     0,    32
   };
 
-  const unsigned char
+  const signed char
   EvalParser::yycheck_[] =
   {
-       4,     5,     8,    10,    13,    12,     6,     7,    11,     0,
-      16,    15,    14,    17,    18,     3,    10,    16,    16,     9,
-      12,    10
+       4,     5,     8,    10,     0,    12,    15,    16,     6,     7,
+      16,    15,    13,    17,    18,     6,     7,    11,     3,    14,
+      10,    14,     9,     9,    16,    -1,    10,    -1,    -1,    12
   };
 
   const unsigned char
   EvalParser::yystos_[] =
   {
        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
+      11,     0,     3,    15,    16,    22,    22,    14,    14,    10,
+       9,     9,    16,    23,     6,     7,     6,     7,    10,     8,
+      16,    24,    12
   };
 
   const unsigned char
   EvalParser::yyr1_[] =
   {
        0,    19,    20,    21,    22,    22,    22,    22,    22,    22,
-      23,    24,    24
+      22,    22,    23,    24,    24
   };
 
   const unsigned char
   EvalParser::yyr2_[] =
   {
-       0,     2,     1,     3,     1,     1,     6,     6,     8,     1,
-       1,     1,     1
+       0,     2,     1,     3,     1,     1,     6,     6,     6,     6,
+       8,     1,     1,     1,     1
   };
 
 
@@ -1018,8 +1056,8 @@ namespace isc { namespace eval {
   const unsigned char
   EvalParser::yyrline_[] =
   {
-       0,   101,   101,   104,   111,   116,   121,   127,   133,   138,
-     142,   149,   154
+       0,   106,   106,   109,   116,   121,   126,   132,   138,   151,
+     164,   169,   173,   180,   185
   };
 
   // Print the state stack on the debug stream.
@@ -1054,8 +1092,8 @@ namespace isc { namespace eval {
 
 #line 21 "parser.yy" // lalr1.cc:1167
 } } // isc::eval
-#line 1058 "parser.cc" // lalr1.cc:1167
-#line 161 "parser.yy" // lalr1.cc:1168
+#line 1096 "parser.cc" // lalr1.cc:1167
+#line 192 "parser.yy" // lalr1.cc:1168
 
 void
 isc::eval::EvalParser::error(const location_type& loc,

+ 8 - 6
src/lib/eval/parser.h

@@ -45,12 +45,13 @@
 #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;
 
-#line 54 "parser.h" // lalr1.cc:392
+#line 55 "parser.h" // lalr1.cc:392
 
 # include <cassert>
 # include <cstdlib> // std::abort
@@ -127,7 +128,7 @@ using namespace isc::eval;
 
 #line 21 "parser.yy" // lalr1.cc:392
 namespace isc { namespace eval {
-#line 131 "parser.h" // lalr1.cc:392
+#line 132 "parser.h" // lalr1.cc:392
 
 
 
@@ -514,7 +515,7 @@ namespace isc { namespace eval {
 
 
     /// Build a parser object.
-    EvalParser (EvalContext& ctx_yyarg);
+    EvalParser (EvalContext& ctx_yyarg, const Option::Universe& option_universe_yyarg);
     virtual ~EvalParser ();
 
     /// Parse.
@@ -597,7 +598,7 @@ namespace isc { namespace eval {
   // number is the opposite.  If YYTABLE_NINF, syntax error.
   static const unsigned char yytable_[];
 
-  static const unsigned char yycheck_[];
+  static const signed char yycheck_[];
 
   // YYSTOS[STATE-NUM] -- The (internal number of the) accessing
   // symbol of state STATE-NUM.
@@ -717,7 +718,7 @@ namespace isc { namespace eval {
     enum
     {
       yyeof_ = 0,
-      yylast_ = 21,     ///< Last index in yytable_.
+      yylast_ = 29,     ///< Last index in yytable_.
       yynnts_ = 6,  ///< Number of nonterminal symbols.
       yyfinal_ = 11, ///< Termination state number.
       yyterror_ = 1,
@@ -728,6 +729,7 @@ namespace isc { namespace eval {
 
     // User arguments.
     EvalContext& ctx;
+    const Option::Universe& option_universe;
   };
 
   // Symbol number corresponding to token number t.
@@ -1083,7 +1085,7 @@ namespace isc { namespace eval {
 
 #line 21 "parser.yy" // lalr1.cc:392
 } } // isc::eval
-#line 1087 "parser.h" // lalr1.cc:392
+#line 1089 "parser.h" // lalr1.cc:392
 
 
 

+ 31 - 0
src/lib/eval/parser.yy

@@ -25,6 +25,7 @@
 #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;
@@ -39,6 +40,10 @@ using namespace isc::eval;
 {
 # include "eval_context.h"
 }
+// Option universe: DHCPv4 or DHCPv6. This is required to use correct option
+// definition set to map option names to codes.
+%parse-param { const Option::Universe& option_universe }
+
 %define api.token.prefix {TOKEN_}
 %token
   END  0  "end of file"
@@ -130,6 +135,32 @@ string_expr : STRING
                       TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL));
                       ctx.expression.push_back(opt);
                   }
+            | OPTION "[" STRING "]" DOT TEXT
+                  {
+                      try {
+                          // This may result in exception if the specified
+                          // name is unknown.
+                          TokenPtr opt(new TokenOption($3, option_universe,
+                                                       TokenOption::TEXTUAL));
+                          ctx.expression.push_back(opt);
+
+                      } catch (const std::exception& ex) {
+                          ctx.error(@3, ex.what());
+                      }
+                  }
+            | OPTION "[" STRING "]" DOT HEX
+                  {
+                      try {
+                          // This may result in exception if the specified
+                          // name is unknown.
+                          TokenPtr opt(new TokenOption($3, option_universe,
+                                                       TokenOption::HEXADECIMAL));
+                          ctx.expression.push_back(opt);
+
+                      } catch (const std::exception& ex) {
+                          ctx.error(@3, ex.what());
+                      }
+                  }
             | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
                   {
                       TokenPtr sub(new TokenSubstring());

+ 25 - 15
src/lib/eval/tests/context_unittest.cc

@@ -16,6 +16,7 @@
 #include <eval/token.h>
 #include <eval/eval_context.h>
 #include <eval/token.h>
+#include <dhcp/option.h>
 #include <dhcp/pkt4.h>
 
 #include <boost/shared_ptr.hpp>
@@ -94,7 +95,7 @@ public:
     /// @brief checks if the given expression raises the expected message
     /// when it is parsed.
     void checkError(const string& expr, const string& msg) {
-        EvalContext eval;
+        EvalContext eval(Option::V4);
         parsed_ = false;
         try {
             parsed_ = eval.parseString(expr);
@@ -115,15 +116,15 @@ public:
 // Test the parsing of a basic expression
 TEST_F(EvalContextTest, basic) {
 
-    EvalContext tmp;
+    EvalContext eval(Option::V4);
 
-    EXPECT_NO_THROW(parsed_ = tmp.parseString("option[123].text == 'MSFT'"));
+    EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'MSFT'"));
     EXPECT_TRUE(parsed_);
 }
 
 // Test the parsing of a string terminal
 TEST_F(EvalContextTest, string) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
     EXPECT_TRUE(parsed_);
@@ -140,7 +141,7 @@ TEST_F(EvalContextTest, string) {
 // Test the parsing of a basic expression using integers
 TEST_F(EvalContextTest, integer) {
 
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
     EXPECT_NO_THROW(parsed_ =
         eval.parseString("substring(option[123].text, 0, 2) == '42'"));
@@ -149,7 +150,7 @@ TEST_F(EvalContextTest, integer) {
 
 // Test the parsing of a hexstring terminal
 TEST_F(EvalContextTest, hexstring) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("0x666f6f == 'foo'"));
     EXPECT_TRUE(parsed_);
@@ -164,7 +165,7 @@ TEST_F(EvalContextTest, hexstring) {
 // Test the parsing of a hexstring terminal with an odd number of
 // hexadecimal digits
 TEST_F(EvalContextTest, oddHexstring) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("0X7 == 'foo'"));
     EXPECT_TRUE(parsed_);
@@ -178,7 +179,7 @@ TEST_F(EvalContextTest, oddHexstring) {
 
 // Test the parsing of an equal expression
 TEST_F(EvalContextTest, equal) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
     EXPECT_TRUE(parsed_);
@@ -196,7 +197,7 @@ TEST_F(EvalContextTest, equal) {
 
 // Test the parsing of an option terminal
 TEST_F(EvalContextTest, option) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'foo'"));
     EXPECT_TRUE(parsed_);
@@ -204,9 +205,20 @@ TEST_F(EvalContextTest, option) {
     checkTokenOption(eval.expression.at(0), 123);
 }
 
+// Test parsing of an option identified by name.
+TEST_F(EvalContextTest, optionWithName) {
+    EvalContext eval(Option::V4);
+
+    // Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++.
+    EXPECT_NO_THROW(parsed_ = eval.parseString("option['host-name'].text == 'foo'"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(3, eval.expression.size());
+    checkTokenOption(eval.expression.at(0), 12);
+}
+
 // Test parsing of an option represented as hexadecimal string.
 TEST_F(EvalContextTest, optionHex) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].hex == 0x666F6F"));
     EXPECT_TRUE(parsed_);
@@ -216,7 +228,7 @@ TEST_F(EvalContextTest, optionHex) {
 
 // Test the parsing of a substring expression
 TEST_F(EvalContextTest, substring) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
     EXPECT_NO_THROW(parsed_ =
         eval.parseString("substring('foobar',2,all) == 'obar'"));
@@ -289,13 +301,11 @@ TEST_F(EvalContextTest, parseErrors) {
                "<string>:1.7: syntax error, "
                "unexpected (, expecting [");
     checkError("option['ab'].text == 'foo'",
-               "<string>:1.8-11: syntax error, "
-               "unexpected constant string, "
-               "expecting integer");
+               "<string>:1.8-11: option 'ab' is not defined");
     checkError("option[0xa].text == 'ab'",
                "<string>:1.8-10: syntax error, "
                "unexpected constant hexstring, "
-               "expecting integer");
+               "expecting constant string or integer");
     checkError("substring('foobar') == 'f'",
                "<string>:1.19: syntax error, "
                "unexpected ), expecting \",\"");

+ 116 - 0
src/lib/eval/tests/token_unittest.cc

@@ -14,6 +14,9 @@
 
 #include <config.h>
 #include <eval/token.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option.h>
+#include <dhcp/option_definition.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/dhcp4.h>
@@ -52,6 +55,13 @@ public:
         pkt6_->addOption(option_str6_);
     }
 
+    /// @brief Destructor.
+    ///
+    /// Removes any option definitions registered in runtime.
+    virtual ~TokenTest() {
+        LibDHCP::clearRuntimeOptionDefs();
+    }
+
     TokenPtr t_; ///< Just a convenience pointer
 
     ValueStack values_; ///< evaluated values will be stored here
@@ -62,6 +72,23 @@ public:
     OptionPtr option_str4_; ///< A string option for DHCPv4
     OptionPtr option_str6_; ///< A string option for DHCPv6
 
+    /// @brief Create definitions of DHCPv4 options used by unit tests.
+    void createOptionDefinitions4() {
+        OptionDefSpaceContainer defs;
+        OptionDefinitionPtr opt_def4(new OptionDefinition("name-hundred4",
+                                                          100, "string"));
+        defs.addItem(opt_def4, "dhcp4");
+        LibDHCP::setRuntimeOptionDefs(defs);
+    }
+
+    /// @brief Create definitions of DHCPv6 options used by unit tests.
+    void createOptionDefinitions6() {
+        OptionDefSpaceContainer defs;
+        OptionDefinitionPtr opt_def6(new OptionDefinition("name-hundred6",
+                                                          100, "string"));
+        defs.addItem(opt_def6, "dhcp6");
+        LibDHCP::setRuntimeOptionDefs(defs);
+    }
 
     /// @brief Verify that the substring eval works properly
     ///
@@ -274,6 +301,28 @@ TEST_F(TokenTest, optionString4) {
     EXPECT_EQ("hundred4", values_.top());
 }
 
+// This test checks if a token representing an option identified by name is
+// able to extract this option from an IPv4 packet and properly store the
+// option's value.
+TEST_F(TokenTest, optionWithNameString4) {
+    // Create definition of option 100 to provide a mapping between option
+    // code and option name.
+    ASSERT_NO_THROW(createOptionDefinitions4());
+
+    // Create token for option referenced by name. The constructor should
+    // map the option name to its code.
+    TokenPtr token;
+    ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred4", Option::V4,
+                                                TokenOption::TEXTUAL)));
+
+    // Evaluate option in the packet.
+    ASSERT_NO_THROW(token->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+
+    // Then the content of the option.
+    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.
@@ -303,6 +352,28 @@ TEST_F(TokenTest, optionHexString4) {
     EXPECT_EQ("0x68756E6472656434", values_.top());
 }
 
+// This test checks if a token representing an option identified by name is
+// able to extract this option from an IPv4 packet and properly store its
+// value in the hexadecimal format.
+TEST_F(TokenTest, optionWithNameHexString4) {
+    // Create definition of option 100 to provide a mapping between option
+    // code and option name.
+    ASSERT_NO_THROW(createOptionDefinitions4());
+
+    // Create token for option referenced by name. The constructor should
+    // map the option name to its code.
+    TokenPtr token;
+    ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred4", Option::V4,
+                                                TokenOption::HEXADECIMAL)));
+
+    // Evaluate option in the packet.
+    ASSERT_NO_THROW(token->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+
+    // Then the content of the option.
+    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) {
@@ -331,6 +402,29 @@ TEST_F(TokenTest, optionString6) {
     EXPECT_EQ("hundred6", values_.top());
 }
 
+// This test checks if a token representing an option identified by name is
+// able to extract this option from an IPv6 packet and properly store the
+// option's value.
+TEST_F(TokenTest, optionWithNameString6) {
+    // Create definition of option 100 to provide a mapping between option
+    // code and option name.
+    ASSERT_NO_THROW(createOptionDefinitions6());
+
+    // Create token for option referenced by name. The constructor should
+    // map the option name to its code.
+    TokenPtr token;
+    ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred6", Option::V6,
+                                                TokenOption::TEXTUAL)));
+
+    // Evaluate option in the packet.
+    ASSERT_NO_THROW(token->evaluate(*pkt6_, values_));
+    ASSERT_EQ(1, values_.size());
+
+    // Then the content of the option.
+    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.
@@ -360,6 +454,28 @@ TEST_F(TokenTest, optionHexString6) {
     EXPECT_EQ("0x68756E6472656436", values_.top());
 }
 
+// This test checks if a token representing an option identified by name is
+// able to extract this option from an IPv6 packet and properly store its
+// value in the hexadecimal format.
+TEST_F(TokenTest, optionWithNameHexString6) {
+    // Create definition of option 100 to provide a mapping between option
+    // code and option name.
+    ASSERT_NO_THROW(createOptionDefinitions6());
+
+    // Create token for option referenced by name. The constructor should
+    // map the option name to its code.
+    TokenPtr token;
+    ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred6", Option::V6,
+                                                TokenOption::HEXADECIMAL)));
+
+    // Evaluate option in the packet.
+    ASSERT_NO_THROW(token->evaluate(*pkt6_, values_));
+    ASSERT_EQ(1, values_.size());
+
+    // Then the content of the option.
+    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) {

+ 22 - 0
src/lib/eval/token.cc

@@ -14,6 +14,8 @@
 
 #include <eval/token.h>
 #include <eval/eval_log.h>
+#include <dhcp/option_definition.h>
+#include <dhcp/libdhcp++.h>
 #include <util/encode/hex.h>
 #include <boost/lexical_cast.hpp>
 #include <cstring>
@@ -62,6 +64,26 @@ TokenHexString::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
     values.push(value_);
 }
 
+TokenOption::TokenOption(const std::string& option_name,
+                         const Option::Universe& option_universe,
+                         const RepresentationType& rep_type)
+    : option_code_(0), representation_type_(rep_type) {
+    OptionDefinitionPtr option_def = LibDHCP::getOptionDef(option_universe,
+                                                           option_name);
+    if (!option_def) {
+        const std::string global_space =
+            (option_universe == Option::V4) ? "dhcp4" : "dhcp6";
+        option_def = LibDHCP::getRuntimeOptionDef(global_space, option_name);
+    }
+
+    if (!option_def) {
+        isc_throw(BadValue, "option '" << option_name << "' is not defined");
+    }
+
+    option_code_ = option_def->getCode();
+}
+
+
 void
 TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
     OptionPtr opt = pkt.getOption(option_code_);

+ 17 - 7
src/lib/eval/token.h

@@ -16,8 +16,10 @@
 #define TOKEN_H
 
 #include <exceptions/exceptions.h>
+#include <dhcp/option.h>
 #include <dhcp/pkt.h>
 #include <stack>
+#include <string>
 
 namespace isc {
 namespace dhcp {
@@ -29,8 +31,8 @@ typedef boost::shared_ptr<Token> TokenPtr;
 
 /// This is a structure that holds an expression converted to RPN
 ///
-/// For example expression: option[123] == 'foo' will be converted to:
-/// [0] = option[123] (TokenOption object)
+/// For example expression: option[123].text == 'foo' will be converted to:
+/// [0] = option[123].text (TokenOption object)
 /// [1] = 'foo' (TokenString object)
 /// [2] = == operator (TokenEqual object)
 typedef std::vector<TokenPtr> Expression;
@@ -160,16 +162,24 @@ public:
     };
 
     /// @brief Constructor that takes an option code as a parameter
-    /// @param option_code code of the option
     ///
-    /// Note: There is no constructor that takes option_name, as it would
-    /// introduce complex dependency of the libkea-eval on libdhcpsrv.
-    ///
-    /// @param option_code code of the option to be represented.
+    /// @param option_code Code of the option to be represented.
     /// @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 Constructor that takes option name as a parameter.
+    ///
+    /// This constructor will throw exception if there is no definition using
+    /// specified option name in libdhcp++.
+    ///
+    /// @param option_name Name of the option to be represented.
+    /// @param option_universe Option universe: DHCPv4 or DHCPv6.
+    /// @param rep_type Token representation type.
+    TokenOption(const std::string& option_name,
+                const Option::Universe& option_universe,
+                const RepresentationType& rep_type);
+
     /// @brief Evaluates the values of the option
     ///
     /// This token represents a value of the option, so this method attempts