Browse Source

[master] Merge branch 'trac4204fd'

Marcin Siodelski 9 years ago
parent
commit
38aebe9566
42 changed files with 1512 additions and 458 deletions
  1. 9 1
      src/bin/dhcp4/json_config_parser.cc
  2. 41 0
      src/bin/dhcp4/tests/config_parser_unittest.cc
  3. 95 0
      src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
  4. 5 0
      src/bin/dhcp6/ctrl_dhcp6_srv.cc
  5. 9 0
      src/bin/dhcp6/json_config_parser.cc
  6. 42 0
      src/bin/dhcp6/tests/config_parser_unittest.cc
  7. 2 1
      src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
  8. 1 0
      src/lib/dhcp/Makefile.am
  9. 73 0
      src/lib/dhcp/libdhcp++.cc
  10. 56 0
      src/lib/dhcp/libdhcp++.h
  11. 11 5
      src/lib/dhcp/option.cc
  12. 9 0
      src/lib/dhcp/option.h
  13. 5 0
      src/lib/dhcp/option_definition.h
  14. 0 0
      src/lib/dhcp/option_space_container.h
  15. 78 0
      src/lib/dhcp/tests/libdhcp++_unittest.cc
  16. 0 1
      src/lib/dhcpsrv/Makefile.am
  17. 1 1
      src/lib/dhcpsrv/cfg_option.h
  18. 7 3
      src/lib/dhcpsrv/cfg_option_def.h
  19. 1 1
      src/lib/dhcpsrv/parsers/client_class_def_parser.cc
  20. 6 0
      src/lib/dhcpsrv/parsers/dhcp_parsers.cc
  21. 1 7
      src/lib/dhcpsrv/parsers/dhcp_parsers.h
  22. 1 1
      src/lib/dhcpsrv/subnet.h
  23. 105 51
      src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc
  24. 9 0
      src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
  25. 51 2
      src/lib/eval/eval_context.cc
  26. 32 3
      src/lib/eval/eval_context.h
  27. 255 203
      src/lib/eval/lexer.cc
  28. 7 0
      src/lib/eval/lexer.ll
  29. 0 1
      src/lib/eval/location.hh
  30. 147 100
      src/lib/eval/parser.cc
  31. 89 16
      src/lib/eval/parser.h
  32. 28 37
      src/lib/eval/parser.yy
  33. 0 1
      src/lib/eval/position.hh
  34. 0 1
      src/lib/eval/stack.hh
  35. 77 14
      src/lib/eval/tests/context_unittest.cc
  36. 1 1
      src/lib/eval/tests/evaluate_unittest.cc
  37. 2 2
      src/lib/eval/tests/token_unittest.cc
  38. 14 5
      src/lib/eval/token.cc
  39. 1 0
      src/lib/util/Makefile.am
  40. 126 0
      src/lib/util/staged_value.h
  41. 1 0
      src/lib/util/tests/Makefile.am
  42. 114 0
      src/lib/util/tests/staged_value_unittest.cc

+ 9 - 1
src/bin/dhcp4/json_config_parser.cc

@@ -22,7 +22,6 @@
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/parsers/client_class_def_parser.h>
 #include <dhcpsrv/parsers/client_class_def_parser.h>
 #include <dhcp4/json_config_parser.h>
 #include <dhcp4/json_config_parser.h>
-#include <dhcpsrv/option_space_container.h>
 #include <dhcpsrv/parsers/dbaccess_parser.h>
 #include <dhcpsrv/parsers/dbaccess_parser.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/expiration_config_parser.h>
 #include <dhcpsrv/parsers/expiration_config_parser.h>
@@ -514,6 +513,12 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
     // Remove any existing timers.
     // Remove any existing timers.
     TimerMgr::instance()->unregisterTimers();
     TimerMgr::instance()->unregisterTimers();
 
 
+    // Revert any runtime option definitions configured so far and not committed.
+    LibDHCP::revertRuntimeOptionDefs();
+    // Let's set empty container in case a user hasn't specified any configuration
+    // for option definitions. This is equivalent to commiting empty container.
+    LibDHCP::setRuntimeOptionDefs(OptionDefSpaceContainer());
+
     // Some of the values specified in the configuration depend on
     // Some of the values specified in the configuration depend on
     // other values. Typically, the values in the subnet4 structure
     // other values. Typically, the values in the subnet4 structure
     // depend on the global values. Also, option values configuration
     // depend on the global values. Also, option values configuration
@@ -700,6 +705,9 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
     // Rollback changes as the configuration parsing failed.
     // Rollback changes as the configuration parsing failed.
     if (rollback) {
     if (rollback) {
         globalContext().reset(new ParserContext(original_context));
         globalContext().reset(new ParserContext(original_context));
+        // Revert to original configuration of runtime option definitions
+        // in the libdhcp++.
+        LibDHCP::revertRuntimeOptionDefs();
         return (answer);
         return (answer);
     }
     }
 
 

+ 41 - 0
src/bin/dhcp4/tests/config_parser_unittest.cc

@@ -1346,6 +1346,11 @@ TEST_F(Dhcp4ParserTest, optionDefIpv4Address) {
     ASSERT_TRUE(status);
     ASSERT_TRUE(status);
     checkResult(status, 0);
     checkResult(status, 0);
 
 
+    // We need to commit option definitions because later in this test we
+    // will be checking if they get removed when "option-def" parameter
+    // is removed from a configuration.
+    LibDHCP::commitRuntimeOptionDefs();
+
     // The option definition should now be available in the CfgMgr.
     // The option definition should now be available in the CfgMgr.
     def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
     def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
     ASSERT_TRUE(def);
     ASSERT_TRUE(def);
@@ -1356,6 +1361,25 @@ TEST_F(Dhcp4ParserTest, optionDefIpv4Address) {
     EXPECT_FALSE(def->getArrayType());
     EXPECT_FALSE(def->getArrayType());
     EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
     EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
     EXPECT_TRUE(def->getEncapsulatedSpace().empty());
     EXPECT_TRUE(def->getEncapsulatedSpace().empty());
+
+    // The copy of the option definition should be available in the libdhcp++.
+    OptionDefinitionPtr def_libdhcp = LibDHCP::getRuntimeOptionDef("isc", 100);
+    ASSERT_TRUE(def_libdhcp);
+
+    // Both definitions should be held in distinct pointers but they should
+    // be equal.
+    EXPECT_TRUE(def_libdhcp != def);
+    EXPECT_TRUE(*def_libdhcp == *def);
+
+    // Let's apply empty configuration. This removes the option definitions
+    // configuration and should result in removal of the option 100 from the
+    // libdhcp++.
+    config = "{ }";
+    json = Element::fromJSON(config);
+    ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    checkResult(status, 0);
+
+    EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("isc", 100));
 }
 }
 
 
 // The goal of this test is to check whether an option definition
 // The goal of this test is to check whether an option definition
@@ -1468,6 +1492,14 @@ TEST_F(Dhcp4ParserTest, optionDefMultiple) {
 // The goal of this test is to verify that the duplicated option
 // The goal of this test is to verify that the duplicated option
 // definition is not accepted.
 // definition is not accepted.
 TEST_F(Dhcp4ParserTest, optionDefDuplicate) {
 TEST_F(Dhcp4ParserTest, optionDefDuplicate) {
+    // Preconfigure libdhcp++ with option definitions. The new configuration
+    // should override it, but when the new configuration fails, it should
+    // revert to this original configuration.
+    OptionDefSpaceContainer defs;
+    OptionDefinitionPtr def(new OptionDefinition("bar", 233, "string"));
+    defs.addItem(def, "isc");
+    LibDHCP::setRuntimeOptionDefs(defs);
+    LibDHCP::commitRuntimeOptionDefs();
 
 
     // Configuration string. Both option definitions have
     // Configuration string. Both option definitions have
     // the same code and belong to the same option space.
     // the same code and belong to the same option space.
@@ -1498,6 +1530,15 @@ TEST_F(Dhcp4ParserTest, optionDefDuplicate) {
     ASSERT_TRUE(status);
     ASSERT_TRUE(status);
     checkResult(status, 1);
     checkResult(status, 1);
     EXPECT_TRUE(errorContainsPosition(status, "<string>"));
     EXPECT_TRUE(errorContainsPosition(status, "<string>"));
+
+    // The new configuration should have inserted option 100, but
+    // once configuration failed (on the duplicate option definition)
+    // the original configuration in libdhcp++ should be reverted.
+    EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("isc", 100));
+    def = LibDHCP::getRuntimeOptionDef("isc", 233);
+    ASSERT_TRUE(def);
+    EXPECT_EQ("bar", def->getName());
+    EXPECT_EQ(233, def->getCode());
 }
 }
 
 
 // The goal of this test is to verify that the option definition
 // The goal of this test is to verify that the option definition

+ 95 - 0
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -1766,6 +1766,101 @@ TEST_F(Dhcpv4SrvTest, matchClassification) {
     EXPECT_FALSE(opt3);
     EXPECT_FALSE(opt3);
 }
 }
 
 
+// Checks if client packets are classified properly using match expressions
+// using option names
+TEST_F(Dhcpv4SrvTest, matchClassificationOptionName) {
+    NakedDhcpv4Srv srv(0);
+
+    // The router class matches incoming packets with foo in a host-name
+    string config = "{ \"interfaces-config\": {"
+        "    \"interfaces\": [ \"*\" ] }, "
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"valid-lifetime\": 4000, "
+        "\"subnet4\": [ "
+        "{   \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], "
+        "    \"subnet\": \"192.0.2.0/24\" } ], "
+        "\"client-classes\": [ "
+        "{   \"name\": \"router\", "
+        "    \"test\": \"option[host-name].text == 'foo'\" } ] }";
+
+    ElementPtr json = Element::fromJSON(config);
+    ConstElementPtr status;
+
+    // Configure the server and make sure the config is accepted
+    EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
+    ASSERT_TRUE(status);
+    comment_ = config::parseAnswer(rcode_, status);
+    ASSERT_EQ(0, rcode_);
+
+    CfgMgr::instance().commit();
+
+    // Create a packet with enough to select the subnet
+    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
+    query->setRemoteAddr(IOAddress("192.0.2.1"));
+
+    // Create and add a host-name option to the query
+    OptionStringPtr hostname(new OptionString(Option::V4, 12, "foo"));
+    ASSERT_TRUE(hostname);
+    query->addOption(hostname);
+
+    // Classify packets
+    srv.classifyPacket(query);
+
+    // The queey should be in the router class
+    EXPECT_TRUE(query->inClass("router"));
+}
+
+// Checks if client packets are classified properly using match expressions
+// using option names and definitions
+TEST_F(Dhcpv4SrvTest, matchClassificationOptionDef) {
+    NakedDhcpv4Srv srv(0);
+
+    // The router class matches incoming packets with foo in a defined
+    // option
+    string config = "{ \"interfaces-config\": {"
+        "    \"interfaces\": [ \"*\" ] }, "
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"valid-lifetime\": 4000, "
+        "\"subnet4\": [ "
+        "{   \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], "
+        "    \"subnet\": \"192.0.2.0/24\" } ], "
+        "\"client-classes\": [ "
+        "{   \"name\": \"router\", "
+        "    \"test\": \"option[my-host-name].text == 'foo'\" } ], "
+        "\"option-def\": [ {"
+        "    \"name\": \"my-host-name\", "
+        "    \"code\": 250, "
+        "    \"type\": \"string\" } ] }";
+
+    ElementPtr json = Element::fromJSON(config);
+    ConstElementPtr status;
+
+    // Configure the server and make sure the config is accepted
+    EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
+    ASSERT_TRUE(status);
+    comment_ = config::parseAnswer(rcode_, status);
+    ASSERT_EQ(0, rcode_);
+
+    CfgMgr::instance().commit();
+
+    // Create a packet with enough to select the subnet
+    Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 1234));
+    query->setRemoteAddr(IOAddress("192.0.2.1"));
+
+    // Create and add a my-host-name option to the query
+    OptionStringPtr hostname(new OptionString(Option::V4, 250, "foo"));
+    ASSERT_TRUE(hostname);
+    query->addOption(hostname);
+
+    // Classify packets
+    srv.classifyPacket(query);
+
+    // The queey should be in the router class
+    EXPECT_TRUE(query->inClass("router"));
+}
+
 // Checks subnet options have the priority over class options
 // Checks subnet options have the priority over class options
 TEST_F(Dhcpv4SrvTest, subnetClassPriority) {
 TEST_F(Dhcpv4SrvTest, subnetClassPriority) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgrTestConfig test_config(true);

+ 5 - 0
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -15,6 +15,7 @@
 #include <config.h>
 #include <config.h>
 #include <cc/data.h>
 #include <cc/data.h>
 #include <config/command_mgr.h>
 #include <config/command_mgr.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6_log.h>
@@ -244,6 +245,10 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
         }
         }
     }
     }
 
 
+    // Finally, we can commit runtime option definitions in libdhcp++. This is
+    // exception free.
+    LibDHCP::commitRuntimeOptionDefs();
+
     return (answer);
     return (answer);
 }
 }
 
 

+ 9 - 0
src/bin/dhcp6/json_config_parser.cc

@@ -755,6 +755,12 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
     // Remove any existing timers.
     // Remove any existing timers.
     TimerMgr::instance()->unregisterTimers();
     TimerMgr::instance()->unregisterTimers();
 
 
+    // Revert any runtime option definitions configured so far and not committed.
+    LibDHCP::revertRuntimeOptionDefs();
+    // Let's set empty container in case a user hasn't specified any configuration
+    // for option definitions. This is equivalent to commiting empty container.
+    LibDHCP::setRuntimeOptionDefs(OptionDefSpaceContainer());
+
     // Some of the values specified in the configuration depend on
     // Some of the values specified in the configuration depend on
     // other values. Typically, the values in the subnet6 structure
     // other values. Typically, the values in the subnet6 structure
     // depend on the global values. Also, option values configuration
     // depend on the global values. Also, option values configuration
@@ -948,6 +954,9 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
     // Rollback changes as the configuration parsing failed.
     // Rollback changes as the configuration parsing failed.
     if (rollback) {
     if (rollback) {
         globalContext().reset(new ParserContext(original_context));
         globalContext().reset(new ParserContext(original_context));
+        // Revert to original configuration of runtime option definitions
+        // in the libdhcp++.
+        LibDHCP::revertRuntimeOptionDefs();
         return (answer);
         return (answer);
     }
     }
 
 

+ 42 - 0
src/bin/dhcp6/tests/config_parser_unittest.cc

@@ -1583,6 +1583,12 @@ TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     ASSERT_TRUE(status);
     ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // We need to commit option definitions because later in this test we
+    // will be checking if they get removed when "option-def" parameter
+    // is removed from a configuration.
+    LibDHCP::commitRuntimeOptionDefs();
 
 
     // The option definition should now be available in the CfgMgr.
     // The option definition should now be available in the CfgMgr.
     def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
     def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
@@ -1593,6 +1599,25 @@ TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
     EXPECT_EQ(100, def->getCode());
     EXPECT_EQ(100, def->getCode());
     EXPECT_FALSE(def->getArrayType());
     EXPECT_FALSE(def->getArrayType());
     EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, def->getType());
     EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, def->getType());
+
+    // The copy of the option definition should be available in the libdhcp++.
+    OptionDefinitionPtr def_libdhcp = LibDHCP::getRuntimeOptionDef("isc", 100);
+    ASSERT_TRUE(def_libdhcp);
+
+    // Both definitions should be held in distinct pointers but they should
+    // be equal.
+    EXPECT_TRUE(def_libdhcp != def);
+    EXPECT_TRUE(*def_libdhcp == *def);
+
+    // Let's apply empty configuration. This removes the option definitions
+    // configuration and should result in removal of the option 100 from the
+    // libdhcp++.
+    config = "{ }";
+    json = Element::fromJSON(config);
+    ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    checkResult(status, 0);
+
+    EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("isc", 100));
 }
 }
 
 
 // The goal of this test is to check whether an option definition
 // The goal of this test is to check whether an option definition
@@ -1702,6 +1727,14 @@ TEST_F(Dhcp6ParserTest, optionDefMultiple) {
 // The goal of this test is to verify that the duplicated option
 // The goal of this test is to verify that the duplicated option
 // definition is not accepted.
 // definition is not accepted.
 TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
 TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
+    // Preconfigure libdhcp++ with option definitions. The new configuration
+    // should override it, but when the new configuration fails, it should
+    // revert to this original configuration.
+    OptionDefSpaceContainer defs;
+    OptionDefinitionPtr def(new OptionDefinition("bar", 233, "string"));
+    defs.addItem(def, "isc");
+    LibDHCP::setRuntimeOptionDefs(defs);
+    LibDHCP::commitRuntimeOptionDefs();
 
 
     // Configuration string. Both option definitions have
     // Configuration string. Both option definitions have
     // the same code and belong to the same option space.
     // the same code and belong to the same option space.
@@ -1732,6 +1765,15 @@ TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
     ASSERT_TRUE(status);
     ASSERT_TRUE(status);
     checkResult(status, 1);
     checkResult(status, 1);
     EXPECT_TRUE(errorContainsPosition(status, "<string>"));
     EXPECT_TRUE(errorContainsPosition(status, "<string>"));
+
+    // The new configuration should have inserted option 100, but
+    // once configuration failed (on the duplicate option definition)
+    // the original configuration in libdhcp++ should be reverted.
+    EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("isc", 100));
+    def = LibDHCP::getRuntimeOptionDef("isc", 233);
+    ASSERT_TRUE(def);
+    EXPECT_EQ("bar", def->getName());
+    EXPECT_EQ(233, def->getCode());
 }
 }
 
 
 // The goal of this test is to verify that the option definition
 // The goal of this test is to verify that the option definition

+ 2 - 1
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -1805,6 +1805,7 @@ TEST_F(Dhcpv6SrvTest, docsisClientClassification) {
 }
 }
 
 
 // Checks if client packets are classified properly using match expressions.
 // Checks if client packets are classified properly using match expressions.
+// Note option names and definitions are used.
 TEST_F(Dhcpv6SrvTest, matchClassification) {
 TEST_F(Dhcpv6SrvTest, matchClassification) {
     IfaceMgrTestConfig test_config(true);
     IfaceMgrTestConfig test_config(true);
 
 
@@ -1834,7 +1835,7 @@ TEST_F(Dhcpv6SrvTest, matchClassification) {
         "    \"option-data\": ["
         "    \"option-data\": ["
         "        {    \"name\": \"ipv6-forwarding\", "
         "        {    \"name\": \"ipv6-forwarding\", "
         "             \"data\": \"true\" } ], "
         "             \"data\": \"true\" } ], "
-        "    \"test\": \"option[1234].text == 'foo'\" } ] }";
+        "    \"test\": \"option[host-name].text == 'foo'\" } ] }";
     ASSERT_NO_THROW(configure(config));
     ASSERT_NO_THROW(configure(config));
 
 
     // Create packets with enough to select the subnet
     // Create packets with enough to select the subnet

+ 1 - 0
src/lib/dhcp/Makefile.am

@@ -44,6 +44,7 @@ libkea_dhcp___la_SOURCES += option_data_types.cc option_data_types.h
 libkea_dhcp___la_SOURCES += option_definition.cc option_definition.h
 libkea_dhcp___la_SOURCES += option_definition.cc option_definition.h
 libkea_dhcp___la_SOURCES += option_opaque_data_tuples.cc option_opaque_data_tuples.h
 libkea_dhcp___la_SOURCES += option_opaque_data_tuples.cc option_opaque_data_tuples.h
 libkea_dhcp___la_SOURCES += option_space.cc option_space.h
 libkea_dhcp___la_SOURCES += option_space.cc option_space.h
+libkea_dhcp___la_SOURCES += option_space_container.h
 libkea_dhcp___la_SOURCES += option_string.cc option_string.h
 libkea_dhcp___la_SOURCES += option_string.cc option_string.h
 libkea_dhcp___la_SOURCES += protocol_util.cc protocol_util.h
 libkea_dhcp___la_SOURCES += protocol_util.cc protocol_util.h
 libkea_dhcp___la_SOURCES += pkt.cc pkt.h
 libkea_dhcp___la_SOURCES += pkt.cc pkt.h

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

@@ -32,6 +32,8 @@
 #include <boost/shared_array.hpp>
 #include <boost/shared_array.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 
 
+#include <list>
+
 using namespace std;
 using namespace std;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace isc::util;
 using namespace isc::util;
@@ -52,6 +54,10 @@ VendorOptionDefContainers LibDHCP::vendor4_defs_;
 
 
 VendorOptionDefContainers LibDHCP::vendor6_defs_;
 VendorOptionDefContainers LibDHCP::vendor6_defs_;
 
 
+// Static container with option definitions created in runtime.
+StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
+
+
 // Those two vendor classes are used for cable modems:
 // Those two vendor classes are used for cable modems:
 
 
 /// DOCSIS3.0 compatible cable modem
 /// DOCSIS3.0 compatible cable modem
@@ -194,6 +200,66 @@ LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
     return (OptionDefinitionPtr());
     return (OptionDefinitionPtr());
 }
 }
 
 
+OptionDefinitionPtr
+LibDHCP::getRuntimeOptionDef(const std::string& space, const uint16_t code) {
+    OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
+    const OptionDefContainerTypeIndex& index = container->get<1>();
+    const OptionDefContainerTypeRange& range = index.equal_range(code);
+    if (range.first != range.second) {
+        return (*range.first);
+    }
+
+    return (OptionDefinitionPtr());
+}
+
+OptionDefinitionPtr
+LibDHCP::getRuntimeOptionDef(const std::string& space, const std::string& name) {
+    OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
+    const OptionDefContainerNameIndex& index = container->get<2>();
+    const OptionDefContainerNameRange& range = index.equal_range(name);
+    if (range.first != range.second) {
+        return (*range.first);
+    }
+
+    return (OptionDefinitionPtr());
+}
+
+OptionDefContainerPtr
+LibDHCP::getRuntimeOptionDefs(const std::string& space) {
+    return (runtime_option_defs_.getValue().getItems(space));
+}
+
+void
+LibDHCP::setRuntimeOptionDefs(const OptionDefSpaceContainer& defs) {
+    OptionDefSpaceContainer defs_copy;
+    std::list<std::string> option_space_names = defs.getOptionSpaceNames();
+    for (std::list<std::string>::const_iterator name = option_space_names.begin();
+         name != option_space_names.end(); ++name) {
+        OptionDefContainerPtr container = defs.getItems(*name);
+        for (OptionDefContainer::const_iterator def = container->begin();
+             def != container->end(); ++def) {
+            OptionDefinitionPtr def_copy(new OptionDefinition(**def));
+            defs_copy.addItem(def_copy, *name);
+        }
+    }
+    runtime_option_defs_ = defs_copy;
+}
+
+void
+LibDHCP::clearRuntimeOptionDefs() {
+    runtime_option_defs_.reset();
+}
+
+void
+LibDHCP::revertRuntimeOptionDefs() {
+    runtime_option_defs_.revert();
+}
+
+void
+LibDHCP::commitRuntimeOptionDefs() {
+    runtime_option_defs_.commit();
+}
+
 bool
 bool
 LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
 LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
     if (u == Option::V6) {
     if (u == Option::V6) {
@@ -260,7 +326,14 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
     OptionDefContainer option_defs;
     OptionDefContainer option_defs;
     if (option_space == "dhcp6") {
     if (option_space == "dhcp6") {
         option_defs = LibDHCP::getOptionDefs(Option::V6);
         option_defs = LibDHCP::getOptionDefs(Option::V6);
+    } else {
+        OptionDefContainerPtr option_defs_ptr =
+            LibDHCP::getRuntimeOptionDefs(option_space);
+        if (option_defs_ptr) {
+            option_defs = *option_defs_ptr;
+        }
     }
     }
+
     // @todo Once we implement other option spaces we should add else clause
     // @todo Once we implement other option spaces we should add else clause
     // here and gather option definitions for them. For now leaving option_defs
     // here and gather option definitions for them. For now leaving option_defs
     // empty will imply creation of generic Option.
     // empty will imply creation of generic Option.

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

@@ -16,8 +16,10 @@
 #define LIBDHCP_H
 #define LIBDHCP_H
 
 
 #include <dhcp/option_definition.h>
 #include <dhcp/option_definition.h>
+#include <dhcp/option_space_container.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
 #include <util/buffer.h>
 #include <util/buffer.h>
+#include <util/staged_value.h>
 
 
 #include <iostream>
 #include <iostream>
 #include <string>
 #include <string>
@@ -90,6 +92,36 @@ public:
                                                   const uint32_t vendor_id,
                                                   const uint32_t vendor_id,
                                                   const std::string& name);
                                                   const std::string& name);
 
 
+
+    /// @brief Returns runtime (non-standard) option definition by space and
+    /// option code.
+    ///
+    /// @param space Option space name.
+    /// @param code Option code.
+    ///
+    /// @return Pointer to option definition or NULL if it doesn't exist.
+    static OptionDefinitionPtr getRuntimeOptionDef(const std::string& space,
+                                                   const uint16_t code);
+
+    /// @brief Returns runtime (non-standard) option definition by space and
+    /// option name.
+    ///
+    /// @param space Option space name.
+    /// @param name Option name.
+    ///
+    /// @return Pointer to option definition or NULL if it doesn't exist.
+    static OptionDefinitionPtr getRuntimeOptionDef(const std::string& space,
+                                                   const std::string& name);
+
+    /// @brief Returns runtime (non-standard) option definitions for specified
+    /// option space name.
+    ///
+    /// @param space Option space name.
+    ///
+    /// @return Pointer to the container holding option definitions or NULL.
+    static OptionDefContainerPtr
+    getRuntimeOptionDefs(const std::string& space);
+
     /// @brief Check if the specified option is a standard option.
     /// @brief Check if the specified option is a standard option.
     ///
     ///
     /// @param u universe (V4 or V6)
     /// @param u universe (V4 or V6)
@@ -256,6 +288,27 @@ public:
                                        const OptionBuffer& buf,
                                        const OptionBuffer& buf,
                                        isc::dhcp::OptionCollection& options);
                                        isc::dhcp::OptionCollection& options);
 
 
+
+    /// @brief Copies option definitions created at runtime.
+    ///
+    /// Copied option definitions will be used as "runtime" option definitions.
+    /// A typical use case is to set option definitions specified by the user
+    /// in the server configuration. These option definitions should be removed
+    /// or replaced with new option definitions upon reconfiguration.
+    ///
+    /// @param defs Const reference to a container holding option definitions
+    /// grouped by option spaces.
+    static void setRuntimeOptionDefs(const OptionDefSpaceContainer& defs);
+
+    /// @brief Removes runtime option definitions.
+    static void clearRuntimeOptionDefs();
+
+    /// @brief Reverts uncommited changes to runtime option definitions.
+    static void revertRuntimeOptionDefs();
+
+    /// @brief Commits runtime option definitions.
+    static void commitRuntimeOptionDefs();
+
 private:
 private:
 
 
     /// Initialize standard DHCPv4 option definitions.
     /// Initialize standard DHCPv4 option definitions.
@@ -301,6 +354,9 @@ private:
 
 
     /// Container for v6 vendor option definitions
     /// Container for v6 vendor option definitions
     static VendorOptionDefContainers vendor6_defs_;
     static VendorOptionDefContainers vendor6_defs_;
+
+    /// Container for additional option defnitions created in runtime.
+    static util::StagedValue<OptionDefSpaceContainer> runtime_option_defs_;
 };
 };
 
 
 }
 }

+ 11 - 5
src/lib/dhcp/option.cc

@@ -220,8 +220,8 @@ Option::toString() {
     return (toText(0));
     return (toText(0));
 }
 }
 
 
-std::string
-Option::toHexString(const bool include_header) {
+std::vector<uint8_t>
+Option::toBinary(const bool include_header) {
     OutputBuffer buf(len());
     OutputBuffer buf(len());
     try {
     try {
         // If the option is too long, exception will be thrown. We allow
         // If the option is too long, exception will be thrown. We allow
@@ -233,12 +233,18 @@ Option::toHexString(const bool include_header) {
                   " of option " << getType() << ": " << ex.what());
                   " of option " << getType() << ": " << ex.what());
     }
     }
     const uint8_t* option_data = static_cast<const uint8_t*>(buf.getData());
     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
     // Assign option data to a vector, with or without option header depending
     // on the value of "include_header" flag.
     // on the value of "include_header" flag.
-    option_vec.assign(option_data + (include_header ? 0 : getHeaderLen()),
-                      option_data + buf.getLength());
+    std::vector<uint8_t> option_vec(option_data + (include_header ? 0 : getHeaderLen()),
+                                    option_data + buf.getLength());
+    return (option_vec);
+}
+
+std::string
+Option::toHexString(const bool include_header) {
+    // Prepare binary version of the option.
+    std::vector<uint8_t> option_vec = toBinary(include_header);
 
 
     // Return hexadecimal representation prepended with 0x or empty string
     // Return hexadecimal representation prepended with 0x or empty string
     // if option has no payload and the header fields are excluded.
     // if option has no payload and the header fields are excluded.

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

@@ -216,6 +216,15 @@ public:
     /// @return string that represents the value of the option.
     /// @return string that represents the value of the option.
     virtual std::string toString();
     virtual std::string toString();
 
 
+    /// @brief Returns binary representation of the 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 Vector holding binary representation of the option.
+    virtual std::vector<uint8_t> toBinary(const bool include_header = false);
+
     /// @brief Returns string containing hexadecimal representation of option.
     /// @brief Returns string containing hexadecimal representation of option.
     ///
     ///
     /// @param include_header Boolean flag which indicates if the output should
     /// @param include_header Boolean flag which indicates if the output should

+ 5 - 0
src/lib/dhcp/option_definition.h

@@ -17,6 +17,7 @@
 
 
 #include <dhcp/option.h>
 #include <dhcp/option.h>
 #include <dhcp/option_data_types.h>
 #include <dhcp/option_data_types.h>
+#include <dhcp/option_space_container.h>
 
 
 #include <boost/multi_index/hashed_index.hpp>
 #include <boost/multi_index/hashed_index.hpp>
 #include <boost/multi_index/mem_fun.hpp>
 #include <boost/multi_index/mem_fun.hpp>
@@ -766,6 +767,10 @@ typedef OptionDefContainer::nth_index<2>::type OptionDefContainerNameIndex;
 typedef std::pair<OptionDefContainerNameIndex::const_iterator,
 typedef std::pair<OptionDefContainerNameIndex::const_iterator,
                   OptionDefContainerNameIndex::const_iterator> OptionDefContainerNameRange;
                   OptionDefContainerNameIndex::const_iterator> OptionDefContainerNameRange;
 
 
+typedef OptionSpaceContainer<
+    OptionDefContainer, OptionDefinitionPtr, std::string
+> OptionDefSpaceContainer;
+
 
 
 } // namespace isc::dhcp
 } // namespace isc::dhcp
 } // namespace isc
 } // namespace isc

src/lib/dhcpsrv/option_space_container.h → src/lib/dhcp/option_space_container.h


+ 78 - 0
src/lib/dhcp/tests/libdhcp++_unittest.cc

@@ -147,6 +147,60 @@ public:
         return (OptionBuffer(opt_data, opt_data + sizeof(opt_data)));
         return (OptionBuffer(opt_data, opt_data + sizeof(opt_data)));
     }
     }
 
 
+    /// @brief Create option definitions and store in the container.
+    ///
+    /// @param spaces_num Number of option spaces to be created.
+    /// @param defs_num Number of option definitions to be created for
+    /// each option space.
+    /// @param [out] defs Container to which option definitions should be
+    /// added.
+    static void createRuntimeOptionDefs(const uint16_t spaces_num,
+                                        const uint16_t defs_num,
+                                        OptionDefSpaceContainer& defs) {
+        for (uint16_t space = 0; space < spaces_num; ++space) {
+            std::ostringstream space_name;
+            space_name << "option-space-" << space;
+            for (uint16_t code = 0; code < defs_num; ++code) {
+                std::ostringstream name;
+                name << "name-for-option-" << code;
+                OptionDefinitionPtr opt_def(new OptionDefinition(name.str(),
+                                                                 code, "string"));
+                defs.addItem(opt_def, space_name.str());
+            }
+        }
+    }
+
+    /// @brief Test if runtime option definitions have been added.
+    ///
+    /// This method uses the same naming conventions for space names and
+    /// options names as @c createRuntimeOptionDefs method.
+    ///
+    /// @param spaces_num Number of option spaces to be tested.
+    /// @param defs_num Number of option definitions that should exist
+    /// in each option space.
+    /// @param should_exist Boolean value which indicates if option
+    /// definitions should exist. If this is false, this function will
+    /// check that they don't exist.
+    static void testRuntimeOptionDefs(const uint16_t spaces_num,
+                                      const uint16_t defs_num,
+                                      const bool should_exist) {
+        for (uint16_t space = 0; space < spaces_num; ++space) {
+            std::ostringstream space_name;
+            space_name << "option-space-" << space;
+            for (uint16_t code = 0; code < defs_num; ++code) {
+                std::ostringstream name;
+                name << "name-for-option-" << code;
+                OptionDefinitionPtr opt_def =
+                    LibDHCP::getRuntimeOptionDef(space_name.str(), name.str());
+                if (should_exist) {
+                    ASSERT_TRUE(opt_def);
+                } else {
+                    ASSERT_FALSE(opt_def);
+                }
+            }
+        }
+    }
+
 private:
 private:
 
 
     /// @brief Test DHCPv4 or DHCPv6 option definition.
     /// @brief Test DHCPv4 or DHCPv6 option definition.
@@ -1319,4 +1373,28 @@ TEST_F(LibDhcpTest, vendorClass6) {
     EXPECT_EQ("eRouter1.0", vclass->getTuple(0).getText());
     EXPECT_EQ("eRouter1.0", vclass->getTuple(0).getText());
 }
 }
 
 
+// This test verifies that it is possible to add runtime option definitions,
+// retrieve them and remove them.
+TEST_F(LibDhcpTest, setRuntimeOptionDefs) {
+    // Create option definitions in 5 namespaces.
+    OptionDefSpaceContainer defs;
+    createRuntimeOptionDefs(5, 100, defs);
+
+    // Apply option definitions.
+    ASSERT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+    // Retrieve all inserted option definitions.
+    testRuntimeOptionDefs(5, 100, true);
+
+    // Attempting to retrieve non existing definitions.
+    EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("option-space-non-existent", 1));
+    EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("option-space-0", 145));
+
+    // Remove all runtime option definitions.
+    ASSERT_NO_THROW(LibDHCP::clearRuntimeOptionDefs());
+
+    // All option definitions should be gone now.
+    testRuntimeOptionDefs(5, 100, false);
+}
+
 } // end of anonymous space
 } // end of anonymous space

+ 0 - 1
src/lib/dhcpsrv/Makefile.am

@@ -132,7 +132,6 @@ libkea_dhcpsrv_la_SOURCES += ncr_generator.cc ncr_generator.h
 if HAVE_PGSQL
 if HAVE_PGSQL
 libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
 libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
 endif
 endif
-libkea_dhcpsrv_la_SOURCES += option_space_container.h
 libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
 libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
 libkea_dhcpsrv_la_SOURCES += srv_config.cc srv_config.h
 libkea_dhcpsrv_la_SOURCES += srv_config.cc srv_config.h
 libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h
 libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h

+ 1 - 1
src/lib/dhcpsrv/cfg_option.h

@@ -16,8 +16,8 @@
 #define CFG_OPTION_H
 #define CFG_OPTION_H
 
 
 #include <dhcp/option.h>
 #include <dhcp/option.h>
+#include <dhcp/option_space_container.h>
 #include <dhcpsrv/key_from_key.h>
 #include <dhcpsrv/key_from_key.h>
-#include <dhcpsrv/option_space_container.h>
 #include <boost/multi_index_container.hpp>
 #include <boost/multi_index_container.hpp>
 #include <boost/multi_index/hashed_index.hpp>
 #include <boost/multi_index/hashed_index.hpp>
 #include <boost/multi_index/sequenced_index.hpp>
 #include <boost/multi_index/sequenced_index.hpp>

+ 7 - 3
src/lib/dhcpsrv/cfg_option_def.h

@@ -16,7 +16,7 @@
 #define CFG_OPTION_DEF_H
 #define CFG_OPTION_DEF_H
 
 
 #include <dhcp/option_definition.h>
 #include <dhcp/option_definition.h>
-#include <dhcpsrv/option_space_container.h>
+#include <dhcp/option_space_container.h>
 #include <string>
 #include <string>
 
 
 namespace isc {
 namespace isc {
@@ -120,14 +120,18 @@ public:
     OptionDefinitionPtr get(const std::string& option_space,
     OptionDefinitionPtr get(const std::string& option_space,
                             const std::string& option_name) const;
                             const std::string& option_name) const;
 
 
+    /// @brief Returns reference to container holding option definitions.
+    const OptionDefSpaceContainer& getContainer() const {
+        return (option_definitions_);
+    }
+
 private:
 private:
 
 
     /// @brief A collection of option definitions.
     /// @brief A collection of option definitions.
     ///
     ///
     /// The option definitions stored in this container can be accessed
     /// The option definitions stored in this container can be accessed
     /// using the option space name they belong to.
     /// using the option space name they belong to.
-    OptionSpaceContainer<OptionDefContainer, OptionDefinitionPtr,
-                         std::string> option_definitions_;
+    OptionDefSpaceContainer option_definitions_;
 
 
 };
 };
 
 

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

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

+ 6 - 0
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -786,6 +786,12 @@ OptionDefParser::build(ConstElementPtr option_def) {
         isc_throw(DhcpConfigError, ex.what() << " ("
         isc_throw(DhcpConfigError, ex.what() << " ("
                   << option_def->getPosition() << ")");
                   << option_def->getPosition() << ")");
     }
     }
+
+    // All definitions have been prepared. Put them as runtime options into
+    // the libdhcp++.
+    const OptionDefSpaceContainer& container =
+        CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->getContainer();
+    LibDHCP::setRuntimeOptionDefs(container);
 }
 }
 
 
 void
 void

+ 1 - 7
src/lib/dhcpsrv/parsers/dhcp_parsers.h

@@ -18,10 +18,10 @@
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <cc/data.h>
 #include <cc/data.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_definition.h>
+#include <dhcp/option_space_container.h>
 #include <dhcpsrv/d2_client_cfg.h>
 #include <dhcpsrv/d2_client_cfg.h>
 #include <dhcpsrv/cfg_iface.h>
 #include <dhcpsrv/cfg_iface.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_option.h>
-#include <dhcpsrv/option_space_container.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/parsers/dhcp_config_parser.h>
 #include <dhcpsrv/parsers/dhcp_config_parser.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
@@ -36,12 +36,6 @@
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
-/// @brief Storage for option definitions.
-typedef OptionSpaceContainer<OptionDefContainer,
-    OptionDefinitionPtr, std::string> OptionDefStorage;
-/// @brief Shared pointer to option definitions storage.
-typedef boost::shared_ptr<OptionDefStorage> OptionDefStoragePtr;
-
 /// Collection of containers holding option spaces. Each container within
 /// Collection of containers holding option spaces. Each container within
 /// a particular option space holds so-called option descriptors.
 /// a particular option space holds so-called option descriptors.
 typedef OptionSpaceContainer<OptionContainer, OptionDescriptor,
 typedef OptionSpaceContainer<OptionContainer, OptionDescriptor,

+ 1 - 1
src/lib/dhcpsrv/subnet.h

@@ -18,8 +18,8 @@
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <dhcp/option.h>
 #include <dhcp/option.h>
 #include <dhcp/classify.h>
 #include <dhcp/classify.h>
+#include <dhcp/option_space_container.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_option.h>
-#include <dhcpsrv/option_space_container.h>
 #include <dhcpsrv/pool.h>
 #include <dhcpsrv/pool.h>
 #include <dhcpsrv/triplet.h>
 #include <dhcpsrv/triplet.h>
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease.h>

+ 105 - 51
src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc

@@ -15,6 +15,8 @@
 #include <config.h>
 #include <config.h>
 
 
 #include <cc/data.h>
 #include <cc/data.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option.h>
 #include <dhcp/option_string.h>
 #include <dhcp/option_string.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/parsers/client_class_def_parser.h>
 #include <dhcpsrv/parsers/client_class_def_parser.h>
@@ -32,6 +34,61 @@ using namespace isc::dhcp;
 
 
 namespace {
 namespace {
 
 
+/// @brief Test fixture class for @c ExpressionParser.
+class ExpressionParserTest : public ::testing::Test {
+protected:
+
+    /// @brief Test that validate expression can be evaluated against v4 or
+    /// v6 packet.
+    ///
+    /// Verifies that given a valid expression, the ExpressionParser
+    /// produces an Expression which can be evaluated against a v4 or v6
+    /// packet.
+    ///
+    /// @param universe V4 or V6.
+    /// @param expression Textual representation of the expression to be
+    /// evaluated.
+    /// @param option_string String data to be placed in the hostname
+    /// option, being placed in the packet used for evaluation.
+    /// @tparam Type of the packet: @c Pkt4 or @c Pkt6.
+    template<typename PktType>
+    void testValidExpression(const Option::Universe& universe,
+                             const std::string& expression,
+                             const std::string& option_string) {
+        ParserContextPtr context(new ParserContext(universe));
+        ExpressionParserPtr parser;
+        ExpressionPtr parsed_expr;
+
+        // Turn config into elements.  This may emit exceptions.
+        ElementPtr config_element = Element::fromJSON(expression);
+
+        // Create the parser.
+        ASSERT_NO_THROW(parser.reset(new ExpressionParser("", parsed_expr,
+                                                      context)));
+        // Expression should parse and commit.
+        ASSERT_NO_THROW(parser->build(config_element));
+        ASSERT_NO_THROW(parser->commit());
+
+        // Parsed expression should exist.
+        ASSERT_TRUE(parsed_expr);
+
+        // Build a packet that will fail evaluation.
+        boost::shared_ptr<PktType> pkt(new PktType(universe == Option::V4 ?
+                                                   DHCPDISCOVER : DHCPV6_SOLICIT,
+                                                   123));
+        EXPECT_FALSE(evaluate(*parsed_expr, *pkt));
+
+        // Now add the option so it will pass. Use a standard option carrying a
+        // single string value, i.e. hostname for DHCPv4 and bootfile url for
+        // DHCPv6.
+        OptionPtr opt(new OptionString(universe, universe == Option::V4 ?
+                                       DHO_HOST_NAME : D6O_BOOTFILE_URL,
+                                       option_string));
+        pkt->addOption(opt);
+        EXPECT_TRUE(evaluate(*parsed_expr, *pkt));
+    }
+};
+
 /// @brief Test fixture class for @c ClientClassDefParser.
 /// @brief Test fixture class for @c ClientClassDefParser.
 class ClientClassDefParserTest : public ::testing::Test {
 class ClientClassDefParserTest : public ::testing::Test {
 protected:
 protected:
@@ -113,69 +170,66 @@ protected:
 
 
 // Verifies that given a valid expression, the ExpressionParser
 // Verifies that given a valid expression, the ExpressionParser
 // produces an Expression which can be evaluated against a v4 packet.
 // produces an Expression which can be evaluated against a v4 packet.
-TEST(ExpressionParserTest, validExpression4) {
-    ParserContextPtr context(new ParserContext(Option::V4));
-    ExpressionParserPtr parser;
-    ExpressionPtr parsed_expr;
-
-    // Turn config into elements.  This may emit exceptions.
-    std::string cfg_txt = "\"option[100].text == 'hundred4'\"";
-    ElementPtr config_element = Element::fromJSON(cfg_txt);
-
-    // Create the parser.
-    ASSERT_NO_THROW(parser.reset(new ExpressionParser("", parsed_expr,
-                                                      context)));
-    // Expression should parse and commit.
-    ASSERT_NO_THROW(parser->build(config_element));
-    ASSERT_NO_THROW(parser->commit());
+TEST_F(ExpressionParserTest, validExpression4) {
+    testValidExpression<Pkt4>(Option::V4, "\"option[12].text == 'hundred4'\"",
+                              "hundred4");
+}
 
 
-    // Parsed expression should exist.
-    ASSERT_TRUE(parsed_expr);
+// Verifies that the option name can be used in the evaluated expression.
+TEST_F(ExpressionParserTest, validExpressionWithOptionName4) {
+    testValidExpression<Pkt4>(Option::V4,
+                              "\"option[host-name].text == 'hundred4'\"",
+                              "hundred4");
+}
 
 
-    // Build a packet that will fail evaluation.
-    Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 123));
-    EXPECT_FALSE(evaluate(*parsed_expr, *pkt4));
+// Verifies that given a valid expression using .hex operator for option, the
+// ExpressionParser produces an Expression which can be evaluated against
+// a v4 packet.
+TEST_F(ExpressionParserTest, validExpressionWithHex4) {
+    testValidExpression<Pkt4>(Option::V4, "\"option[12].hex == 0x68756E6472656434\"",
+                              "hundred4");
+}
 
 
-    // Now add the option so it will pass.
-    OptionPtr opt(new OptionString(Option::V4, 100, "hundred4"));
-    pkt4->addOption(opt);
-    EXPECT_TRUE(evaluate(*parsed_expr, *pkt4));
+// Verifies that the option name can be used together with .hex operator in
+// the evaluated expression.
+TEST_F(ExpressionParserTest, validExpressionWithOptionNameAndHex4) {
+    testValidExpression<Pkt6>(Option::V4,
+                              "\"option[host-name].text == 0x68756E6472656434\"",
+                              "hundred4");
 }
 }
 
 
 // Verifies that given a valid expression, the ExpressionParser
 // Verifies that given a valid expression, the ExpressionParser
 // produces an Expression which can be evaluated against a v6 packet.
 // produces an Expression which can be evaluated against a v6 packet.
-TEST(ExpressionParserTest, validExpression6) {
-    ParserContextPtr context(new ParserContext(Option::V6));
-    ExpressionParserPtr parser;
-    ExpressionPtr parsed_expr;
-
-    // Turn config into elements.  This may emit exceptions.
-    std::string cfg_txt = "\"option[100].text == 'hundred6'\"";
-    ElementPtr config_element = Element::fromJSON(cfg_txt);
-
-    // Create the parser.
-    ASSERT_NO_THROW(parser.reset(new ExpressionParser("", parsed_expr,
-                                                      context)));
-    // Expression should parse and commit.
-    ASSERT_NO_THROW(parser->build(config_element));
-    ASSERT_NO_THROW(parser->commit());
-
-    // Parsed expression should exist.
-    ASSERT_TRUE(parsed_expr);
+TEST_F(ExpressionParserTest, validExpression6) {
+    testValidExpression<Pkt6>(Option::V6, "\"option[59].text == 'hundred6'\"",
+                              "hundred6");
+}
 
 
-    // Build a packet that will fail evaluation.
-    Pkt6Ptr pkt6(new Pkt6(DHCPDISCOVER, 123));
-    EXPECT_FALSE(evaluate(*parsed_expr, *pkt6));
+// Verifies that the option name can be used in the evaluated expression.
+TEST_F(ExpressionParserTest, validExpressionWithOptionName6) {
+    testValidExpression<Pkt6>(Option::V6,
+                              "\"option[bootfile-url].text == 'hundred6'\"",
+                              "hundred6");
+}
 
 
-    // Now add the option so it will pass.
-    OptionPtr opt(new OptionString(Option::V6, 100, "hundred6"));
-    pkt6->addOption(opt);
-    EXPECT_TRUE(evaluate(*parsed_expr, *pkt6));
+// Verifies that given a valid expression using .hex operator for option, the
+// ExpressionParser produces an Expression which can be evaluated against
+// a v6 packet.
+TEST_F(ExpressionParserTest, validExpressionWithHex6) {
+    testValidExpression<Pkt6>(Option::V6, "\"option[59].hex == 0x68756E6472656436\"",
+                              "hundred6");
 }
 }
 
 
+// Verifies that the option name can be used together with .hex operator in
+// the evaluated expression.
+TEST_F(ExpressionParserTest, validExpressionWithOptionNameAndHex6) {
+    testValidExpression<Pkt6>(Option::V6,
+                              "\"option[bootfile-url].text == 0x68756E6472656436\"",
+                              "hundred6");
+}
 
 
 // Verifies that an the ExpressionParser only accepts StringElements.
 // Verifies that an the ExpressionParser only accepts StringElements.
-TEST(ExpressionParserTest, invalidExpressionElement) {
+TEST_F(ExpressionParserTest, invalidExpressionElement) {
     ParserContextPtr context(new ParserContext(Option::V4));
     ParserContextPtr context(new ParserContext(Option::V4));
     ExpressionParserPtr parser;
     ExpressionParserPtr parser;
     ExpressionPtr parsed_expr;
     ExpressionPtr parsed_expr;
@@ -196,7 +250,7 @@ TEST(ExpressionParserTest, invalidExpressionElement) {
 // is not intended to be an exhaustive test or expression syntax.
 // is not intended to be an exhaustive test or expression syntax.
 // It is simply to ensure that if the parser fails, it does so
 // It is simply to ensure that if the parser fails, it does so
 // Properly.
 // Properly.
-TEST(ExpressionParserTest, expressionSyntaxError) {
+TEST_F(ExpressionParserTest, expressionSyntaxError) {
     ParserContextPtr context(new ParserContext(Option::V4));
     ParserContextPtr context(new ParserContext(Option::V4));
     ExpressionParserPtr parser;
     ExpressionParserPtr parser;
     ExpressionPtr parsed_expr;
     ExpressionPtr parsed_expr;

+ 9 - 0
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

@@ -495,6 +495,15 @@ TEST_F(ParseConfigTest, basicOptionDefTest) {
     EXPECT_FALSE(def->getArrayType());
     EXPECT_FALSE(def->getArrayType());
     EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
     EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
     EXPECT_TRUE(def->getEncapsulatedSpace().empty());
     EXPECT_TRUE(def->getEncapsulatedSpace().empty());
+
+    // Check if libdhcp++ runtime options have been updated.
+    OptionDefinitionPtr def_libdhcp = LibDHCP::getRuntimeOptionDef("isc", 100);
+    ASSERT_TRUE(def_libdhcp);
+
+    // The LibDHCP should return a separate instance of the option definition
+    // but the values should be equal.
+    EXPECT_TRUE(def_libdhcp != def);
+    EXPECT_TRUE(*def_libdhcp == *def);
 }
 }
 
 
 /// @brief Check minimal parsing of option definitions.
 /// @brief Check minimal parsing of option definitions.

+ 51 - 2
src/lib/eval/eval_context.cc

@@ -12,13 +12,18 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
+#include <dhcp/option.h>
+#include <dhcp/option_definition.h>
+#include <dhcp/libdhcp++.h>
 #include <eval/eval_context.h>
 #include <eval/eval_context.h>
 #include <eval/parser.h>
 #include <eval/parser.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
+#include <boost/lexical_cast.hpp>
 #include <fstream>
 #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)
 {
 {
 }
 }
 
 
@@ -51,6 +56,50 @@ EvalContext::error (const std::string& what)
     isc_throw(EvalParseError, what);
     isc_throw(EvalParseError, what);
 }
 }
 
 
+uint16_t
+EvalContext::convertOptionCode(const std::string& option_code,
+                               const isc::eval::location& loc)
+{
+    int n = 0;
+    try {
+        n  = boost::lexical_cast<int>(option_code);
+    } catch (const boost::bad_lexical_cast &) {
+        // This can't happen...
+        error(loc, "Option code has invalid value in " + option_code);
+    }
+    if (option_universe_ == Option::V6) {
+        if (n < 0 || n > 65535) {
+            error(loc, "Option code has invalid value in "
+                      + option_code + ". Allowed range: 0..65535");
+        }
+    } else {
+        if (n < 0 || n > 255) {
+            error(loc, "Option code has invalid value in "
+                      + option_code + ". Allowed range: 0..255");
+        }
+    }
+    return (static_cast<uint16_t>(n));
+}
+
+uint16_t
+EvalContext::convertOptionName(const std::string& option_name,
+                               const isc::eval::location& loc)
+{
+    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) {
+        error(loc, "option '" + option_name + "' is not defined");
+    }
+
+    return (option_def->getCode());
+}
+
 void
 void
 EvalContext::fatal (const std::string& what)
 EvalContext::fatal (const std::string& what)
 {
 {

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

@@ -42,7 +42,11 @@ class EvalContext
 {
 {
 public:
 public:
     /// @brief Default constructor.
     /// @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
     /// @brief destructor
     virtual ~EvalContext();
     virtual ~EvalContext();
@@ -55,7 +59,7 @@ public:
 
 
     /// @brief Method called after the last tokens are scanned from a string.
     /// @brief Method called after the last tokens are scanned from a string.
     void scanStringEnd();
     void scanStringEnd();
-    
+
     /// @brief Run the parser on the string specified.
     /// @brief Run the parser on the string specified.
     ///
     ///
     /// @param str string to be written
     /// @param str string to be written
@@ -86,13 +90,38 @@ public:
     /// This is for should not happen but fatal errors
     /// This is for should not happen but fatal errors
     static void fatal(const std::string& what);
     static void fatal(const std::string& what);
 
 
+    /// @brief Option code convertion
+    ///
+    /// @param option_code a string representing the integer code
+    /// @param loc the location of the token
+    /// @result the option code
+    /// @throw calls the syntax error function if the value is no in
+    ///        the range 0..255 or 0..65535
+    uint16_t convertOptionCode(const std::string& option_code,
+                               const isc::eval::location& loc);
+
+    /// @brief Option name convertion
+    ///
+    /// @param option_name the option name
+    /// @param loc the location of the token
+    /// @result the option code
+    /// @throw calls the syntax error function if the name cannot be resolved
+    uint16_t convertOptionName(const std::string& option_name,
+                               const isc::eval::location& loc);
+
  private:
  private:
     /// @brief Flag determining scanner debugging.
     /// @brief Flag determining scanner debugging.
     bool trace_scanning_;
     bool trace_scanning_;
 
 
     /// @brief Flag determing parser debugging.
     /// @brief Flag determing parser debugging.
     bool trace_parsing_;
     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
 }; // end of isc::eval namespace

+ 255 - 203
src/lib/eval/lexer.cc

@@ -18,7 +18,7 @@
 #define FLEX_SCANNER
 #define FLEX_SCANNER
 #define YY_FLEX_MAJOR_VERSION 2
 #define YY_FLEX_MAJOR_VERSION 2
 #define YY_FLEX_MINOR_VERSION 5
 #define YY_FLEX_MINOR_VERSION 5
-#define YY_FLEX_SUBMINOR_VERSION 39
+#define YY_FLEX_SUBMINOR_VERSION 35
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #define FLEX_BETA
 #define FLEX_BETA
 #endif
 #endif
@@ -72,6 +72,7 @@ typedef int16_t flex_int16_t;
 typedef uint16_t flex_uint16_t;
 typedef uint16_t flex_uint16_t;
 typedef int32_t flex_int32_t;
 typedef int32_t flex_int32_t;
 typedef uint32_t flex_uint32_t;
 typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
 #else
 #else
 typedef signed char flex_int8_t;
 typedef signed char flex_int8_t;
 typedef short int flex_int16_t;
 typedef short int flex_int16_t;
@@ -79,6 +80,7 @@ typedef int flex_int32_t;
 typedef unsigned char flex_uint8_t; 
 typedef unsigned char flex_uint8_t; 
 typedef unsigned short int flex_uint16_t;
 typedef unsigned short int flex_uint16_t;
 typedef unsigned int flex_uint32_t;
 typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
 
 
 /* Limits of integral types. */
 /* Limits of integral types. */
 #ifndef INT8_MIN
 #ifndef INT8_MIN
@@ -109,8 +111,6 @@ typedef unsigned int flex_uint32_t;
 #define UINT32_MAX             (4294967295U)
 #define UINT32_MAX             (4294967295U)
 #endif
 #endif
 
 
-#endif /* ! C99 */
-
 #endif /* ! FLEXINT_H */
 #endif /* ! FLEXINT_H */
 
 
 /* %endif */
 /* %endif */
@@ -225,18 +225,11 @@ extern FILE *yyin, *yyout;
      */
      */
     #define  YY_LESS_LINENO(n) \
     #define  YY_LESS_LINENO(n) \
             do { \
             do { \
-                int yyl;\
+                yy_size_t yyl;\
                 for ( yyl = n; yyl < yyleng; ++yyl )\
                 for ( yyl = n; yyl < yyleng; ++yyl )\
                     if ( yytext[yyl] == '\n' )\
                     if ( yytext[yyl] == '\n' )\
                         --yylineno;\
                         --yylineno;\
             }while(0)
             }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. */
 /* Return all but the first "n" matched characters back to the input stream. */
 #define yyless(n) \
 #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 */
 /* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */
 /* Begin user sect3 */
 /* Begin user sect3 */
 
 
-#define yywrap() 1
+#define yywrap(n) 1
 #define YY_SKIP_YYWRAP
 #define YY_SKIP_YYWRAP
 
 
 #define FLEX_DEBUG
 #define FLEX_DEBUG
@@ -445,8 +438,6 @@ int yylineno = 1;
 extern char *yytext;
 extern char *yytext;
 #define yytext_ptr yytext
 #define yytext_ptr yytext
 
 
-/* %% [1.5] DFA */
-
 /* %if-c-only Standard (non-C++) definition */
 /* %if-c-only Standard (non-C++) definition */
 
 
 static yy_state_type yy_get_previous_state (void );
 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 \
 #define YY_DO_BEFORE_ACTION \
 	(yytext_ptr) = yy_bp; \
 	(yytext_ptr) = yy_bp; \
 /* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\
 /* %% [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_hold_char) = *yy_cp; \
 	*yy_cp = '\0'; \
 	*yy_cp = '\0'; \
 /* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\
 /* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\
 	(yy_c_buf_p) = yy_cp;
 	(yy_c_buf_p) = yy_cp;
 
 
 /* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */
 /* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */
-#define YY_NUM_RULES 19
-#define YY_END_OF_BUFFER 20
+#define YY_NUM_RULES 20
+#define YY_END_OF_BUFFER 21
 /* This struct is not used in this scanner,
 /* This struct is not used in this scanner,
    but its presence is necessary. */
    but its presence is necessary. */
 struct yy_trans_info
 struct yy_trans_info
@@ -478,14 +469,27 @@ struct yy_trans_info
 	flex_int32_t yy_verify;
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	flex_int32_t yy_nxt;
 	};
 	};
-static yyconst flex_int16_t yy_accept[52] =
+static yyconst flex_int16_t yy_acclist[90] =
+    {   0,
+       21,   19,   20,    1,   19,   20,    2,   20,   19,   20,
+       14,   19,   20,   15,   19,   20,   18,   19,   20,   19,
+       20,   13,   19,   20,    5,   19,   20,    5,   19,   20,
+       19,   20,   19,   20,16390,   16,   19,   20,   17,   19,
+       20,   19,   20,16390,   19,   20,16390,   19,   20,16390,
+       19,   20,16390,   19,   20,16390,    1,    2,    3,    5,
+        7,16390, 8198,16390,16390,16390,16390,16390,    4,   12,
+    16390,   10,16390,16390,16390,16390,16390,16390,    9,16390,
+    16390,16390,    8,16390,16390,16390,16390,   11,16390
+    } ;
+
+static yyconst flex_int16_t yy_accept[58] =
     {   0,
     {   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
+        1,    1,    1,    2,    4,    7,    9,   11,   14,   17,
+       20,   22,   25,   28,   31,   33,   36,   39,   42,   45,
+       48,   51,   54,   57,   58,   59,   59,   60,   61,   61,
+       62,   62,   62,   63,   64,   65,   66,   67,   68,   69,
+       70,   72,   74,   75,   76,   77,   78,   79,   81,   82,
+       83,   85,   86,   87,   88,   90,   90
     } ;
     } ;
 
 
 static yyconst flex_int32_t yy_ec[256] =
 static yyconst flex_int32_t yy_ec[256] =
@@ -497,13 +501,13 @@ static yyconst flex_int32_t yy_ec[256] =
         6,    1,    1,    7,    8,    9,    1,   10,   11,   11,
         6,    1,    1,    7,    8,    9,    1,   10,   11,   11,
        11,   11,   11,   11,   11,   11,   11,    1,    1,    1,
        11,   11,   11,   11,   11,   11,   11,    1,    1,    1,
        12,    1,    1,    1,   13,   13,   13,   13,   13,   13,
        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,   14,    1,    1,
-       15,    1,   16,    1,    1,    1,   17,   18,   13,   13,
+       14,   14,   14,   14,   14,   14,   14,   14,   14,   14,
+       14,   14,   14,   14,   14,   14,   14,   15,   14,   14,
+       16,    1,   17,    1,   18,    1,   19,   20,   13,   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,
+       21,   13,   22,   23,   24,   14,   14,   25,   14,   26,
+       27,   28,   14,   29,   30,   31,   32,   14,   14,   33,
+       14,   14,    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,    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,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -520,88 +524,110 @@ static yyconst flex_int32_t yy_ec[256] =
         1,    1,    1,    1,    1
         1,    1,    1,    1,    1
     } ;
     } ;
 
 
-static yyconst flex_int32_t yy_meta[32] =
+static yyconst flex_int32_t yy_meta[34] =
     {   0,
     {   0,
-        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
+        1,    2,    3,    1,    1,    1,    1,    2,    1,    4,
+        4,    1,    4,    2,    2,    1,    2,    2,    4,    4,
+        4,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+        2,    2,    2
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_base[54] =
+static yyconst flex_int16_t yy_base[60] =
     {   0,
     {   0,
-        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
+        0,    0,  119,  120,  116,  114,  112,  120,  120,  120,
+       24,  120,   26,   28,  103,   58,  120,  120,   75,   19,
+       17,   18,   26,  112,  110,  108,  120,   38,    0,  120,
+       50,   54,   86,  120,   85,   21,   33,   43,   37,    0,
+       80,   76,   48,   44,   55,   57,   56,   66,   63,   65,
+       60,   71,   72,   77,   40,  120,  102,  105,   51
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_def[54] =
+static yyconst flex_int16_t yy_def[60] =
     {   0,
     {   0,
-       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
+       56,    1,   56,   56,   56,   56,   57,   56,   56,   56,
+       56,   56,   56,   56,   56,   58,   56,   56,   58,   19,
+       19,   19,   19,   56,   56,   57,   56,   56,   59,   56,
+       56,   19,   19,   56,   19,   19,   19,   19,   19,   59,
+       19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,    0,   56,   56,   56
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_nxt[105] =
+static yyconst flex_int16_t yy_nxt[154] =
     {   0,
     {   0,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
-       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
+       14,   15,   16,   16,   16,   17,   18,    4,   19,   16,
+       16,   16,   20,   16,   16,   16,   21,   16,   16,   22,
+       23,   16,   16,   28,   28,   28,   28,   28,   28,   36,
+       29,   33,   33,   33,   37,   33,   39,   28,   28,   38,
+       33,   31,   31,   42,   40,   56,   56,   33,   29,   31,
+       31,   33,   44,   43,   33,   32,   34,   33,   33,   45,
+       56,   46,   33,   47,   34,   32,   31,   31,   33,   33,
+       33,   33,   32,   49,   33,   48,   50,   33,   51,   33,
+       33,   34,   32,   52,   53,   33,   33,   54,   55,   35,
+
+       33,   33,   26,   26,   33,   26,   33,   33,   33,   41,
+       33,   27,   25,   24,   30,   27,   25,   24,   56,    3,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_chk[105] =
+static yyconst flex_int16_t yy_chk[154] =
     {   0,
     {   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,    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,    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
+        1,    1,    1,   11,   11,   13,   13,   14,   14,   20,
+       13,   21,   22,   20,   21,   36,   23,   28,   28,   22,
+       23,   31,   31,   36,   59,   32,   32,   37,   13,   16,
+       16,   39,   38,   37,   55,   16,   31,   38,   44,   39,
+       32,   43,   43,   44,   16,   16,   19,   19,   32,   45,
+       47,   46,   19,   46,   51,   45,   47,   49,   49,   50,
+       48,   19,   19,   50,   52,   52,   53,   53,   54,   19,
+
+       42,   54,   57,   57,   41,   57,   58,   58,   58,   35,
+       33,   26,   25,   24,   15,    7,    6,    5,    3,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56,   56,   56,   56,   56,   56,   56,   56,
+       56,   56,   56
     } ;
     } ;
 
 
 /* Table of booleans, true if rule could match eol. */
 /* Table of booleans, true if rule could match eol. */
-static yyconst flex_int32_t yy_rule_can_match_eol[20] =
+static yyconst flex_int32_t yy_rule_can_match_eol[21] =
     {   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;
+0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0,     };
 
 
 extern int yy_flex_debug;
 extern int yy_flex_debug;
 int yy_flex_debug = 1;
 int yy_flex_debug = 1;
 
 
-static yyconst flex_int16_t yy_rule_linenum[19] =
+static yyconst flex_int16_t yy_rule_linenum[20] =
     {   0,
     {   0,
-       86,   90,   96,  106,  112,  126,  127,  128,  129,  130,
-      131,  132,  133,  134,  135,  136,  137,  139
+       86,   90,   96,  106,  112,  126,  133,  134,  135,  136,
+      137,  138,  139,  140,  141,  142,  143,  144,  146
     } ;
     } ;
 
 
-/* The intent behind this definition is that it'll catch
- * any uses of REJECT which flex missed.
- */
-#define REJECT reject_used_but_not_detected
+static yy_state_type *yy_state_buf=0, *yy_state_ptr=0;
+static char *yy_full_match;
+static int yy_lp;
+static int yy_looking_for_trail_begin = 0;
+static int yy_full_lp;
+static int *yy_full_state;
+#define YY_TRAILING_MASK 0x2000
+#define YY_TRAILING_HEAD_MASK 0x4000
+#define REJECT \
+{ \
+*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ \
+yy_cp = (yy_full_match); /* restore poss. backed-over text */ \
+(yy_lp) = (yy_full_lp); /* restore orig. accepting pos. */ \
+(yy_state_ptr) = (yy_full_state); /* restore orig. state */ \
+yy_current_state = *(yy_state_ptr); /* restore curr. state */ \
+++(yy_lp); \
+goto find_rule; \
+}
+
 #define yymore() yymore_used_but_not_detected
 #define yymore() yymore_used_but_not_detected
 #define YY_MORE_ADJ 0
 #define YY_MORE_ADJ 0
 #define YY_RESTORE_YY_MORE_OFFSET
 #define YY_RESTORE_YY_MORE_OFFSET
@@ -665,7 +691,7 @@ static isc::eval::location loc;
 // by moving it ahead by yyleng bytes. yyleng specifies the length of the
 // by moving it ahead by yyleng bytes. yyleng specifies the length of the
 // currently matched token.
 // currently matched token.
 #define YY_USER_ACTION  loc.columns(yyleng);
 #define YY_USER_ACTION  loc.columns(yyleng);
-#line 669 "lexer.cc"
+#line 695 "lexer.cc"
 
 
 #define INITIAL 0
 #define INITIAL 0
 
 
@@ -783,7 +809,7 @@ static int input (void );
 /* This used to be an fputs(), but since the string might contain NUL's,
 /* This used to be an fputs(), but since the string might contain NUL's,
  * we now use fwrite().
  * we now use fwrite().
  */
  */
-#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
 /* %endif */
 /* %endif */
 /* %if-c++-only C++ definition */
 /* %if-c++-only C++ definition */
 /* %endif */
 /* %endif */
@@ -798,7 +824,7 @@ static int input (void );
 	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
 	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
 		{ \
 		{ \
 		int c = '*'; \
 		int c = '*'; \
-		size_t n; \
+		yy_size_t n; \
 		for ( n = 0; n < max_size && \
 		for ( n = 0; n < max_size && \
 			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
 			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
 			buf[n] = (char) c; \
 			buf[n] = (char) c; \
@@ -904,6 +930,17 @@ YY_DECL
 	register char *yy_cp, *yy_bp;
 	register char *yy_cp, *yy_bp;
 	register int yy_act;
 	register int yy_act;
     
     
+/* %% [7.0] user's declarations go here */
+#line 79 "lexer.ll"
+
+
+
+    // Code run each time yylex is called.
+    loc.step();
+
+
+#line 943 "lexer.cc"
+
 	if ( !(yy_init) )
 	if ( !(yy_init) )
 		{
 		{
 		(yy_init) = 1;
 		(yy_init) = 1;
@@ -912,6 +949,12 @@ YY_DECL
 		YY_USER_INIT;
 		YY_USER_INIT;
 #endif
 #endif
 
 
+        /* Create the reject buffer large enough to save one state per allowed character. */
+        if ( ! (yy_state_buf) )
+            (yy_state_buf) = (yy_state_type *)yyalloc(YY_STATE_BUF_SIZE  );
+            if ( ! (yy_state_buf) )
+                YY_FATAL_ERROR( "out of dynamic memory in yylex()" );
+
 		if ( ! (yy_start) )
 		if ( ! (yy_start) )
 			(yy_start) = 1;	/* first start state */
 			(yy_start) = 1;	/* first start state */
 
 
@@ -938,18 +981,6 @@ YY_DECL
 		yy_load_buffer_state( );
 		yy_load_buffer_state( );
 		}
 		}
 
 
-	{
-/* %% [7.0] user's declarations go here */
-#line 79 "lexer.ll"
-
-
-
-    // Code run each time yylex is called.
-    loc.step();
-
-
-#line 952 "lexer.cc"
-
 	while ( 1 )		/* loops until end-of-file is reached */
 	while ( 1 )		/* loops until end-of-file is reached */
 		{
 		{
 /* %% [8.0] yymore()-related code goes here */
 /* %% [8.0] yymore()-related code goes here */
@@ -965,31 +996,66 @@ YY_DECL
 
 
 /* %% [9.0] code to set up and find next match goes here */
 /* %% [9.0] code to set up and find next match goes here */
 		yy_current_state = (yy_start);
 		yy_current_state = (yy_start);
+
+		(yy_state_ptr) = (yy_state_buf);
+		*(yy_state_ptr)++ = yy_current_state;
+
 yy_match:
 yy_match:
 		do
 		do
 			{
 			{
-			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;
-				(yy_last_accepting_cpos) = yy_cp;
-				}
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 				{
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
 				yy_current_state = (int) yy_def[yy_current_state];
-				if ( yy_current_state >= 52 )
+				if ( yy_current_state >= 57 )
 					yy_c = yy_meta[(unsigned int) yy_c];
 					yy_c = yy_meta[(unsigned int) yy_c];
 				}
 				}
 			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
 			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			*(yy_state_ptr)++ = yy_current_state;
 			++yy_cp;
 			++yy_cp;
 			}
 			}
-		while ( yy_current_state != 51 );
-		yy_cp = (yy_last_accepting_cpos);
-		yy_current_state = (yy_last_accepting_state);
+		while ( yy_current_state != 56 );
 
 
 yy_find_action:
 yy_find_action:
 /* %% [10.0] code to find the action number goes here */
 /* %% [10.0] code to find the action number goes here */
-		yy_act = yy_accept[yy_current_state];
+		yy_current_state = *--(yy_state_ptr);
+		(yy_lp) = yy_accept[yy_current_state];
+goto find_rule; /* Shut up GCC warning -Wall */
+find_rule: /* we branch to this label when backing up */
+		for ( ; ; ) /* until we find what rule we matched */
+			{
+			if ( (yy_lp) && (yy_lp) < yy_accept[yy_current_state + 1] )
+				{
+				yy_act = yy_acclist[(yy_lp)];
+				if ( yy_act & YY_TRAILING_HEAD_MASK ||
+				     (yy_looking_for_trail_begin) )
+					{
+					if ( yy_act == (yy_looking_for_trail_begin) )
+						{
+						(yy_looking_for_trail_begin) = 0;
+						yy_act &= ~YY_TRAILING_HEAD_MASK;
+						break;
+						}
+					}
+				else if ( yy_act & YY_TRAILING_MASK )
+					{
+					(yy_looking_for_trail_begin) = yy_act & ~YY_TRAILING_MASK;
+					(yy_looking_for_trail_begin) |= YY_TRAILING_HEAD_MASK;
+					}
+				else
+					{
+					(yy_full_match) = yy_cp;
+					(yy_full_state) = (yy_state_ptr);
+					(yy_full_lp) = (yy_lp);
+					break;
+					}
+				++(yy_lp);
+				goto find_rule;
+				}
+			--yy_cp;
+			yy_current_state = *--(yy_state_ptr);
+			(yy_lp) = yy_accept[yy_current_state];
+			}
 
 
 		YY_DO_BEFORE_ACTION;
 		YY_DO_BEFORE_ACTION;
 
 
@@ -1012,13 +1078,13 @@ do_action:	/* This label is used only to access EOF actions. */
 			{
 			{
 			if ( yy_act == 0 )
 			if ( yy_act == 0 )
 				fprintf( stderr, "--scanner backing up\n" );
 				fprintf( stderr, "--scanner backing up\n" );
-			else if ( yy_act < 19 )
+			else if ( yy_act < 20 )
 				fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
 				fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
 				         (long)yy_rule_linenum[yy_act], yytext );
 				         (long)yy_rule_linenum[yy_act], yytext );
-			else if ( yy_act == 19 )
+			else if ( yy_act == 20 )
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				         yytext );
 				         yytext );
-			else if ( yy_act == 20 )
+			else if ( yy_act == 21 )
 				fprintf( stderr, "--(end of buffer or a NUL)\n" );
 				fprintf( stderr, "--(end of buffer or a NUL)\n" );
 			else
 			else
 				fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
 				fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
@@ -1027,13 +1093,6 @@ do_action:	/* This label is used only to access EOF actions. */
 		switch ( yy_act )
 		switch ( yy_act )
 	{ /* beginning of action switch */
 	{ /* beginning of action switch */
 /* %% [13.0] actions go here */
 /* %% [13.0] actions go here */
-			case 0: /* must back up */
-			/* undo the effects of YY_DO_BEFORE_ACTION */
-			*yy_cp = (yy_hold_char);
-			yy_cp = (yy_last_accepting_cpos);
-			yy_current_state = (yy_last_accepting_state);
-			goto yy_find_action;
-
 case 1:
 case 1:
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 86 "lexer.ll"
 #line 86 "lexer.ll"
@@ -1092,80 +1151,91 @@ YY_RULE_SETUP
 }
 }
 	YY_BREAK
 	YY_BREAK
 case 6:
 case 6:
+/* rule 6 can match eol */
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 126 "lexer.ll"
 #line 126 "lexer.ll"
-return isc::eval::EvalParser::make_EQUAL(loc);
+{
+    // This string specifies option name starting with a letter
+    // and further containing letters, digits, hyphens and
+    // underscores and finishing by letters or digits.
+    return isc::eval::EvalParser::make_OPTION_NAME(yytext, loc);
+}
 	YY_BREAK
 	YY_BREAK
 case 7:
 case 7:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 127 "lexer.ll"
-return isc::eval::EvalParser::make_OPTION(loc);
+#line 133 "lexer.ll"
+return isc::eval::EvalParser::make_EQUAL(loc);
 	YY_BREAK
 	YY_BREAK
 case 8:
 case 8:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 128 "lexer.ll"
-return isc::eval::EvalParser::make_TEXT(loc);
+#line 134 "lexer.ll"
+return isc::eval::EvalParser::make_OPTION(loc);
 	YY_BREAK
 	YY_BREAK
 case 9:
 case 9:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 129 "lexer.ll"
-return isc::eval::EvalParser::make_HEX(loc);
+#line 135 "lexer.ll"
+return isc::eval::EvalParser::make_TEXT(loc);
 	YY_BREAK
 	YY_BREAK
 case 10:
 case 10:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 130 "lexer.ll"
-return isc::eval::EvalParser::make_SUBSTRING(loc);
+#line 136 "lexer.ll"
+return isc::eval::EvalParser::make_HEX(loc);
 	YY_BREAK
 	YY_BREAK
 case 11:
 case 11:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 131 "lexer.ll"
-return isc::eval::EvalParser::make_ALL(loc);
+#line 137 "lexer.ll"
+return isc::eval::EvalParser::make_SUBSTRING(loc);
 	YY_BREAK
 	YY_BREAK
 case 12:
 case 12:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 132 "lexer.ll"
-return isc::eval::EvalParser::make_DOT(loc);
+#line 138 "lexer.ll"
+return isc::eval::EvalParser::make_ALL(loc);
 	YY_BREAK
 	YY_BREAK
 case 13:
 case 13:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 133 "lexer.ll"
-return isc::eval::EvalParser::make_LPAREN(loc);
+#line 139 "lexer.ll"
+return isc::eval::EvalParser::make_DOT(loc);
 	YY_BREAK
 	YY_BREAK
 case 14:
 case 14:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 134 "lexer.ll"
-return isc::eval::EvalParser::make_RPAREN(loc);
+#line 140 "lexer.ll"
+return isc::eval::EvalParser::make_LPAREN(loc);
 	YY_BREAK
 	YY_BREAK
 case 15:
 case 15:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 135 "lexer.ll"
-return isc::eval::EvalParser::make_LBRACKET(loc);
+#line 141 "lexer.ll"
+return isc::eval::EvalParser::make_RPAREN(loc);
 	YY_BREAK
 	YY_BREAK
 case 16:
 case 16:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 136 "lexer.ll"
-return isc::eval::EvalParser::make_RBRACKET(loc);
+#line 142 "lexer.ll"
+return isc::eval::EvalParser::make_LBRACKET(loc);
 	YY_BREAK
 	YY_BREAK
 case 17:
 case 17:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 137 "lexer.ll"
-return isc::eval::EvalParser::make_COMA(loc);
+#line 143 "lexer.ll"
+return isc::eval::EvalParser::make_RBRACKET(loc);
 	YY_BREAK
 	YY_BREAK
 case 18:
 case 18:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 139 "lexer.ll"
+#line 144 "lexer.ll"
+return isc::eval::EvalParser::make_COMA(loc);
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 146 "lexer.ll"
 driver.error (loc, "Invalid character: " + std::string(yytext));
 driver.error (loc, "Invalid character: " + std::string(yytext));
 	YY_BREAK
 	YY_BREAK
 case YY_STATE_EOF(INITIAL):
 case YY_STATE_EOF(INITIAL):
-#line 140 "lexer.ll"
+#line 147 "lexer.ll"
 return isc::eval::EvalParser::make_END(loc);
 return isc::eval::EvalParser::make_END(loc);
 	YY_BREAK
 	YY_BREAK
-case 19:
+case 20:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 141 "lexer.ll"
+#line 148 "lexer.ll"
 ECHO;
 ECHO;
 	YY_BREAK
 	YY_BREAK
-#line 1169 "lexer.cc"
+#line 1239 "lexer.cc"
 
 
 	case YY_END_OF_BUFFER:
 	case YY_END_OF_BUFFER:
 		{
 		{
@@ -1231,8 +1301,7 @@ ECHO;
 			else
 			else
 				{
 				{
 /* %% [14.0] code to do back-up for compressed tables and set up yy_cp goes here */
 /* %% [14.0] code to do back-up for compressed tables and set up yy_cp goes here */
-				yy_cp = (yy_last_accepting_cpos);
-				yy_current_state = (yy_last_accepting_state);
+				yy_cp = (yy_c_buf_p);
 				goto yy_find_action;
 				goto yy_find_action;
 				}
 				}
 			}
 			}
@@ -1296,7 +1365,6 @@ ECHO;
 			"fatal flex scanner internal error--no action found" );
 			"fatal flex scanner internal error--no action found" );
 	} /* end of action switch */
 	} /* end of action switch */
 		} /* end of scanning one token */
 		} /* end of scanning one token */
-	} /* end of user's declarations */
 } /* end of yylex */
 } /* end of yylex */
 /* %ok-for-header */
 /* %ok-for-header */
 
 
@@ -1370,37 +1438,8 @@ static int yy_get_next_buffer (void)
 		while ( num_to_read <= 0 )
 		while ( num_to_read <= 0 )
 			{ /* Not enough room in the buffer - grow it. */
 			{ /* Not enough room in the buffer - grow it. */
 
 
-			/* just a shorter name for the current buffer */
-			YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
-
-			int yy_c_buf_p_offset =
-				(int) ((yy_c_buf_p) - b->yy_ch_buf);
-
-			if ( b->yy_is_our_buffer )
-				{
-				yy_size_t new_size = b->yy_buf_size * 2;
-
-				if ( new_size <= 0 )
-					b->yy_buf_size += b->yy_buf_size / 8;
-				else
-					b->yy_buf_size *= 2;
-
-				b->yy_ch_buf = (char *)
-					/* Include room in for 2 EOB chars. */
-					yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
-				}
-			else
-				/* Can't grow it, we don't own it. */
-				b->yy_ch_buf = 0;
-
-			if ( ! b->yy_ch_buf )
-				YY_FATAL_ERROR(
-				"fatal error - scanner input buffer overflow" );
-
-			(yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
-
-			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
-						number_to_move - 1;
+			YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
 
 
 			}
 			}
 
 
@@ -1466,22 +1505,21 @@ static int yy_get_next_buffer (void)
 /* %% [15.0] code to get the start state into yy_current_state goes here */
 /* %% [15.0] code to get the start state into yy_current_state goes here */
 	yy_current_state = (yy_start);
 	yy_current_state = (yy_start);
 
 
+	(yy_state_ptr) = (yy_state_buf);
+	*(yy_state_ptr)++ = yy_current_state;
+
 	for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
 	for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
 		{
 		{
 /* %% [16.0] code to find the next state goes here */
 /* %% [16.0] code to find the next state goes here */
 		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
 		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
-		if ( yy_accept[yy_current_state] )
-			{
-			(yy_last_accepting_state) = yy_current_state;
-			(yy_last_accepting_cpos) = yy_cp;
-			}
 		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 			{
 			{
 			yy_current_state = (int) yy_def[yy_current_state];
 			yy_current_state = (int) yy_def[yy_current_state];
-			if ( yy_current_state >= 52 )
+			if ( yy_current_state >= 57 )
 				yy_c = yy_meta[(unsigned int) yy_c];
 				yy_c = yy_meta[(unsigned int) yy_c];
 			}
 			}
 		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
 		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		*(yy_state_ptr)++ = yy_current_state;
 		}
 		}
 
 
 	return yy_current_state;
 	return yy_current_state;
@@ -1500,24 +1538,20 @@ static int yy_get_next_buffer (void)
 {
 {
 	register int yy_is_jam;
 	register int yy_is_jam;
     /* %% [17.0] code to find the next state, and perhaps do backing up, goes here */
     /* %% [17.0] code to find the next state, and perhaps do backing up, goes here */
-	register char *yy_cp = (yy_c_buf_p);
 
 
 	register YY_CHAR yy_c = 1;
 	register YY_CHAR yy_c = 1;
-	if ( yy_accept[yy_current_state] )
-		{
-		(yy_last_accepting_state) = yy_current_state;
-		(yy_last_accepting_cpos) = yy_cp;
-		}
 	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 		{
 		{
 		yy_current_state = (int) yy_def[yy_current_state];
 		yy_current_state = (int) yy_def[yy_current_state];
-		if ( yy_current_state >= 52 )
+		if ( yy_current_state >= 57 )
 			yy_c = yy_meta[(unsigned int) yy_c];
 			yy_c = yy_meta[(unsigned int) yy_c];
 		}
 		}
 	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
 	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-	yy_is_jam = (yy_current_state == 51);
+	yy_is_jam = (yy_current_state == 56);
+	if ( ! yy_is_jam )
+		*(yy_state_ptr)++ = yy_current_state;
 
 
-		return yy_is_jam ? 0 : yy_current_state;
+	return yy_is_jam ? 0 : yy_current_state;
 }
 }
 
 
 /* %if-c-only */
 /* %if-c-only */
@@ -1576,7 +1610,7 @@ static int yy_get_next_buffer (void)
 				case EOB_ACT_END_OF_FILE:
 				case EOB_ACT_END_OF_FILE:
 					{
 					{
 					if ( yywrap( ) )
 					if ( yywrap( ) )
-						return EOF;
+						return 0;
 
 
 					if ( ! (yy_did_buffer_switch_on_eof) )
 					if ( ! (yy_did_buffer_switch_on_eof) )
 						YY_NEW_FILE;
 						YY_NEW_FILE;
@@ -1740,6 +1774,17 @@ static void yy_load_buffer_state  (void)
 	yyfree((void *) b  );
 	yyfree((void *) b  );
 }
 }
 
 
+/* %if-c-only */
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* %endif */
+
+/* %if-c++-only */
+/* %endif */
+
 /* Initializes or reinitializes a buffer.
 /* Initializes or reinitializes a buffer.
  * This function is sometimes called more than once on the same buffer,
  * This function is sometimes called more than once on the same buffer,
  * such as during a yyrestart() or at EOF.
  * such as during a yyrestart() or at EOF.
@@ -1980,8 +2025,8 @@ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
 /* %if-c-only */
 /* %if-c-only */
 /** Setup the input buffer state to scan the given bytes. The next call to yylex() will
 /** Setup the input buffer state to scan the given bytes. The next call to yylex() will
  * scan from a @e copy of @a bytes.
  * 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.
  * @return the newly allocated buffer state object.
  */
  */
@@ -1989,8 +2034,7 @@ YY_BUFFER_STATE yy_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len
 {
 {
 	YY_BUFFER_STATE b;
 	YY_BUFFER_STATE b;
 	char *buf;
 	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. */
 	/* Get memory for full buffer, including space for trailing EOB's. */
 	n = _yybytes_len + 2;
 	n = _yybytes_len + 2;
@@ -2158,6 +2202,11 @@ static int yy_init_globals (void)
     (yy_init) = 0;
     (yy_init) = 0;
     (yy_start) = 0;
     (yy_start) = 0;
 
 
+    (yy_state_buf) = 0;
+    (yy_state_ptr) = 0;
+    (yy_full_match) = 0;
+    (yy_lp) = 0;
+
 /* Defined in main.c */
 /* Defined in main.c */
 #ifdef YY_STDINIT
 #ifdef YY_STDINIT
     yyin = stdin;
     yyin = stdin;
@@ -2190,6 +2239,9 @@ int yylex_destroy  (void)
 	yyfree((yy_buffer_stack) );
 	yyfree((yy_buffer_stack) );
 	(yy_buffer_stack) = NULL;
 	(yy_buffer_stack) = NULL;
 
 
+    yyfree ( (yy_state_buf) );
+    (yy_state_buf)  = NULL;
+
     /* Reset the globals. This is important in a non-reentrant scanner so the next time
     /* Reset the globals. This is important in a non-reentrant scanner so the next time
      * yylex() is called, initialization will occur. */
      * yylex() is called, initialization will occur. */
     yy_init_globals( );
     yy_init_globals( );
@@ -2253,7 +2305,7 @@ void yyfree (void * ptr )
 
 
 /* %ok-for-header */
 /* %ok-for-header */
 
 
-#line 141 "lexer.ll"
+#line 148 "lexer.ll"
 
 
 
 
 
 

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

@@ -123,6 +123,13 @@ blank [ \t]
     return isc::eval::EvalParser::make_INTEGER(tmp, loc);
     return isc::eval::EvalParser::make_INTEGER(tmp, loc);
 }
 }
 
 
+[A-Za-z]([-_A-Za-z0-9]*[A-Za-z0-9])?/({blank}|\n)*] {
+    // This string specifies option name starting with a letter
+    // and further containing letters, digits, hyphens and
+    // underscores and finishing by letters or digits.
+    return isc::eval::EvalParser::make_OPTION_NAME(yytext, loc);
+}
+
 "=="        return isc::eval::EvalParser::make_EQUAL(loc);
 "=="        return isc::eval::EvalParser::make_EQUAL(loc);
 "option"    return isc::eval::EvalParser::make_OPTION(loc);
 "option"    return isc::eval::EvalParser::make_OPTION(loc);
 "text"      return isc::eval::EvalParser::make_TEXT(loc);
 "text"      return isc::eval::EvalParser::make_TEXT(loc);

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

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

+ 147 - 100
src/lib/eval/parser.cc

@@ -49,37 +49,11 @@
 
 
 #line 51 "parser.cc" // lalr1.cc:412
 #line 51 "parser.cc" // lalr1.cc:412
 // Unqualified %code blocks.
 // Unqualified %code blocks.
-#line 39 "parser.yy" // lalr1.cc:413
+#line 40 "parser.yy" // lalr1.cc:413
 
 
 # include "eval_context.h"
 # 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 83 "parser.cc" // lalr1.cc:413
+#line 57 "parser.cc" // lalr1.cc:413
 
 
 
 
 #ifndef YY_
 #ifndef YY_
@@ -165,7 +139,7 @@ convert_option_code(const std::string& option_code,
 
 
 #line 21 "parser.yy" // lalr1.cc:479
 #line 21 "parser.yy" // lalr1.cc:479
 namespace isc { namespace eval {
 namespace isc { namespace eval {
-#line 169 "parser.cc" // lalr1.cc:479
+#line 143 "parser.cc" // lalr1.cc:479
 
 
   /* Return YYSTR after stripping away unnecessary quotes and
   /* Return YYSTR after stripping away unnecessary quotes and
      backslashes, so that it's suitable for yyerror.  The heuristic is
      backslashes, so that it's suitable for yyerror.  The heuristic is
@@ -277,13 +251,22 @@ namespace isc { namespace eval {
   {
   {
       switch (that.type_get ())
       switch (that.type_get ())
     {
     {
+      case 25: // option_repr_type
+        value.move< TokenOption::RepresentationType > (that.value);
+        break;
+
       case 15: // "constant string"
       case 15: // "constant string"
       case 16: // "integer"
       case 16: // "integer"
       case 17: // "constant hexstring"
       case 17: // "constant hexstring"
-      case 18: // TOKEN
+      case 18: // "option name"
+      case 19: // TOKEN
         value.move< std::string > (that.value);
         value.move< std::string > (that.value);
         break;
         break;
 
 
+      case 24: // option_code
+        value.move< uint16_t > (that.value);
+        break;
+
       default:
       default:
         break;
         break;
     }
     }
@@ -299,13 +282,22 @@ namespace isc { namespace eval {
     state = that.state;
     state = that.state;
       switch (that.type_get ())
       switch (that.type_get ())
     {
     {
+      case 25: // option_repr_type
+        value.copy< TokenOption::RepresentationType > (that.value);
+        break;
+
       case 15: // "constant string"
       case 15: // "constant string"
       case 16: // "integer"
       case 16: // "integer"
       case 17: // "constant hexstring"
       case 17: // "constant hexstring"
-      case 18: // TOKEN
+      case 18: // "option name"
+      case 19: // TOKEN
         value.copy< std::string > (that.value);
         value.copy< std::string > (that.value);
         break;
         break;
 
 
+      case 24: // option_code
+        value.copy< uint16_t > (that.value);
+        break;
+
       default:
       default:
         break;
         break;
     }
     }
@@ -344,30 +336,51 @@ namespace isc { namespace eval {
     {
     {
             case 15: // "constant string"
             case 15: // "constant string"
 
 
-#line 64 "parser.yy" // lalr1.cc:636
+#line 70 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 350 "parser.cc" // lalr1.cc:636
+#line 342 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
       case 16: // "integer"
       case 16: // "integer"
 
 
-#line 64 "parser.yy" // lalr1.cc:636
+#line 70 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 357 "parser.cc" // lalr1.cc:636
+#line 349 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
       case 17: // "constant hexstring"
       case 17: // "constant hexstring"
 
 
-#line 64 "parser.yy" // lalr1.cc:636
+#line 70 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 364 "parser.cc" // lalr1.cc:636
+#line 356 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 18: // TOKEN
+      case 18: // "option name"
 
 
-#line 64 "parser.yy" // lalr1.cc:636
+#line 70 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 371 "parser.cc" // lalr1.cc:636
+#line 363 "parser.cc" // lalr1.cc:636
+        break;
+
+      case 19: // TOKEN
+
+#line 70 "parser.yy" // lalr1.cc:636
+        { yyoutput << yysym.value.template as< std::string > (); }
+#line 370 "parser.cc" // lalr1.cc:636
+        break;
+
+      case 24: // option_code
+
+#line 70 "parser.yy" // lalr1.cc:636
+        { yyoutput << yysym.value.template as< uint16_t > (); }
+#line 377 "parser.cc" // lalr1.cc:636
+        break;
+
+      case 25: // option_repr_type
+
+#line 70 "parser.yy" // lalr1.cc:636
+        { yyoutput << yysym.value.template as< TokenOption::RepresentationType > (); }
+#line 384 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
 
 
@@ -567,13 +580,22 @@ namespace isc { namespace eval {
          when using variants.  */
          when using variants.  */
         switch (yyr1_[yyn])
         switch (yyr1_[yyn])
     {
     {
+      case 25: // option_repr_type
+        yylhs.value.build< TokenOption::RepresentationType > ();
+        break;
+
       case 15: // "constant string"
       case 15: // "constant string"
       case 16: // "integer"
       case 16: // "integer"
       case 17: // "constant hexstring"
       case 17: // "constant hexstring"
-      case 18: // TOKEN
+      case 18: // "option name"
+      case 19: // TOKEN
         yylhs.value.build< std::string > ();
         yylhs.value.build< std::string > ();
         break;
         break;
 
 
+      case 24: // option_code
+        yylhs.value.build< uint16_t > ();
+        break;
+
       default:
       default:
         break;
         break;
     }
     }
@@ -592,90 +614,111 @@ namespace isc { namespace eval {
           switch (yyn)
           switch (yyn)
             {
             {
   case 3:
   case 3:
-#line 105 "parser.yy" // lalr1.cc:859
+#line 83 "parser.yy" // lalr1.cc:859
     {
     {
                     TokenPtr eq(new TokenEqual());
                     TokenPtr eq(new TokenEqual());
                     ctx.expression.push_back(eq);
                     ctx.expression.push_back(eq);
                 }
                 }
-#line 601 "parser.cc" // lalr1.cc:859
+#line 623 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 4:
   case 4:
-#line 112 "parser.yy" // lalr1.cc:859
+#line 90 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                       ctx.expression.push_back(str);
                   }
                   }
-#line 610 "parser.cc" // lalr1.cc:859
+#line 632 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 5:
   case 5:
-#line 117 "parser.yy" // lalr1.cc:859
+#line 95 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
                       TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(hex);
                       ctx.expression.push_back(hex);
                   }
                   }
-#line 619 "parser.cc" // lalr1.cc:859
+#line 641 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 6:
   case 6:
-#line 122 "parser.yy" // lalr1.cc:859
+#line 100 "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));
+                      TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), yystack_[0].value.as< TokenOption::RepresentationType > ()));
                       ctx.expression.push_back(opt);
                       ctx.expression.push_back(opt);
                   }
                   }
-#line 629 "parser.cc" // lalr1.cc:859
+#line 650 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 7:
   case 7:
-#line 128 "parser.yy" // lalr1.cc:859
+#line 105 "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);
+                      TokenPtr sub(new TokenSubstring());
+                      ctx.expression.push_back(sub);
                   }
                   }
-#line 639 "parser.cc" // lalr1.cc:859
+#line 659 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 8:
-#line 134 "parser.yy" // lalr1.cc:859
+  case 9:
+#line 114 "parser.yy" // lalr1.cc:859
     {
     {
-                      TokenPtr sub(new TokenSubstring());
-                      ctx.expression.push_back(sub);
-                  }
-#line 648 "parser.cc" // lalr1.cc:859
+                     yylhs.value.as< uint16_t > () = ctx.convertOptionCode(yystack_[0].value.as< std::string > (), yystack_[0].location);
+                 }
+#line 667 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 10:
   case 10:
-#line 143 "parser.yy" // lalr1.cc:859
+#line 118 "parser.yy" // lalr1.cc:859
+    {
+                     yylhs.value.as< uint16_t > () = ctx.convertOptionName(yystack_[0].value.as< std::string > (), yystack_[0].location);
+                 }
+#line 675 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 11:
+#line 124 "parser.yy" // lalr1.cc:859
+    {
+                          yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
+                      }
+#line 683 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 12:
+#line 128 "parser.yy" // lalr1.cc:859
+    {
+                          yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
+                      }
+#line 691 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 13:
+#line 134 "parser.yy" // lalr1.cc:859
     {
     {
                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                      ctx.expression.push_back(str);
                      ctx.expression.push_back(str);
                  }
                  }
-#line 657 "parser.cc" // lalr1.cc:859
+#line 700 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 11:
-#line 150 "parser.yy" // lalr1.cc:859
+  case 14:
+#line 141 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                       ctx.expression.push_back(str);
                   }
                   }
-#line 666 "parser.cc" // lalr1.cc:859
+#line 709 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 12:
-#line 155 "parser.yy" // lalr1.cc:859
+  case 15:
+#line 146 "parser.yy" // lalr1.cc:859
     {
     {
                      TokenPtr str(new TokenString("all"));
                      TokenPtr str(new TokenString("all"));
                      ctx.expression.push_back(str);
                      ctx.expression.push_back(str);
                  }
                  }
-#line 675 "parser.cc" // lalr1.cc:859
+#line 718 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
 
 
-#line 679 "parser.cc" // lalr1.cc:859
+#line 722 "parser.cc" // lalr1.cc:859
             default:
             default:
               break;
               break;
             }
             }
@@ -930,74 +973,77 @@ namespace isc { namespace eval {
   }
   }
 
 
 
 
-  const signed char EvalParser::yypact_ninf_ = -10;
+  const signed char EvalParser::yypact_ninf_ = -14;
 
 
   const signed char EvalParser::yytable_ninf_ = -1;
   const signed char EvalParser::yytable_ninf_ = -1;
 
 
   const signed char
   const signed char
   EvalParser::yypact_[] =
   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,    -9,    -5,   -14,   -14,   -14,     8,   -14,     9,   -13,
+      -4,   -14,    -4,   -14,   -14,     0,    11,   -14,    13,     2,
+      10,   -14,    14,   -14,   -14,   -14,    -6,   -14,   -14,     7,
+     -14
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yydefact_[] =
   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,     8,     0,     2,     0,     0,
+       0,     1,     0,     9,    10,     0,     0,     3,     0,     0,
+       0,    13,     0,    11,    12,     6,     0,    15,    14,     0,
+       7
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yypgoto_[] =
   EvalParser::yypgoto_[] =
   {
   {
-     -10,   -10,   -10,    -7,   -10,   -10
+     -14,   -14,   -14,    -3,   -14,   -14,   -14,   -14
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yydefgoto_[] =
   EvalParser::yydefgoto_[] =
   {
   {
-      -1,     6,     7,     8,    20,    26
+      -1,     6,     7,     8,    15,    25,    22,    29
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yytable_[] =
   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,    27,    13,     9,    14,    10,    16,    11,    17,
+      28,     3,    12,     4,    18,     5,    23,    24,    21,    30,
+       0,    19,    20,     0,    26
   };
   };
 
 
-  const unsigned char
+  const signed char
   EvalParser::yycheck_[] =
   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,    16,    13,    18,    11,    10,     0,    12,
+      16,    15,     3,    17,    14,    19,     6,     7,    16,    12,
+      -1,    10,     9,    -1,    10
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yystos_[] =
   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
+       0,     4,     5,    15,    17,    19,    21,    22,    23,    13,
+      11,     0,     3,    16,    18,    24,    23,    23,    14,    10,
+       9,    16,    26,     6,     7,    25,    10,     8,    16,    27,
+      12
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yyr1_[] =
   EvalParser::yyr1_[] =
   {
   {
-       0,    19,    20,    21,    22,    22,    22,    22,    22,    22,
-      23,    24,    24
+       0,    20,    21,    22,    23,    23,    23,    23,    23,    24,
+      24,    25,    25,    26,    27,    27
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yyr2_[] =
   EvalParser::yyr2_[] =
   {
   {
-       0,     2,     1,     3,     1,     1,     6,     6,     8,     1,
-       1,     1,     1
+       0,     2,     1,     3,     1,     1,     6,     8,     1,     1,
+       1,     1,     1,     1,     1,     1
   };
   };
 
 
 
 
@@ -1010,16 +1056,17 @@ namespace isc { namespace eval {
   "\"end of file\"", "error", "$undefined", "\"==\"", "\"option\"",
   "\"end of file\"", "error", "$undefined", "\"==\"", "\"option\"",
   "\"substring\"", "\"text\"", "\"hex\"", "\"all\"", "\".\"", "\",\"",
   "\"substring\"", "\"text\"", "\"hex\"", "\"all\"", "\".\"", "\",\"",
   "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"", "\"integer\"",
   "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"", "\"integer\"",
-  "\"constant hexstring\"", "TOKEN", "$accept", "expression", "bool_expr",
-  "string_expr", "start_expr", "length_expr", YY_NULLPTR
+  "\"constant hexstring\"", "\"option name\"", "TOKEN", "$accept",
+  "expression", "bool_expr", "string_expr", "option_code",
+  "option_repr_type", "start_expr", "length_expr", YY_NULLPTR
   };
   };
 
 
 #if YYDEBUG
 #if YYDEBUG
   const unsigned char
   const unsigned char
   EvalParser::yyrline_[] =
   EvalParser::yyrline_[] =
   {
   {
-       0,   101,   101,   104,   111,   116,   121,   127,   133,   138,
-     142,   149,   154
+       0,    79,    79,    82,    89,    94,    99,   104,   109,   113,
+     117,   123,   127,   133,   140,   145
   };
   };
 
 
   // Print the state stack on the debug stream.
   // Print the state stack on the debug stream.
@@ -1054,8 +1101,8 @@ namespace isc { namespace eval {
 
 
 #line 21 "parser.yy" // lalr1.cc:1167
 #line 21 "parser.yy" // lalr1.cc:1167
 } } // isc::eval
 } } // isc::eval
-#line 1058 "parser.cc" // lalr1.cc:1167
-#line 161 "parser.yy" // lalr1.cc:1168
+#line 1105 "parser.cc" // lalr1.cc:1167
+#line 152 "parser.yy" // lalr1.cc:1168
 
 
 void
 void
 isc::eval::EvalParser::error(const location_type& loc,
 isc::eval::EvalParser::error(const location_type& loc,

+ 89 - 16
src/lib/eval/parser.h

@@ -45,12 +45,13 @@
 #include <string>
 #include <string>
 #include <eval/token.h>
 #include <eval/token.h>
 #include <eval/eval_context_decl.h>
 #include <eval/eval_context_decl.h>
+#include <dhcp/option.h>
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
 
 
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace isc::eval;
 using namespace isc::eval;
 
 
-#line 54 "parser.h" // lalr1.cc:392
+#line 55 "parser.h" // lalr1.cc:392
 
 
 # include <cassert>
 # include <cassert>
 # include <cstdlib> // std::abort
 # include <cstdlib> // std::abort
@@ -127,7 +128,7 @@ using namespace isc::eval;
 
 
 #line 21 "parser.yy" // lalr1.cc:392
 #line 21 "parser.yy" // lalr1.cc:392
 namespace isc { namespace eval {
 namespace isc { namespace eval {
-#line 131 "parser.h" // lalr1.cc:392
+#line 132 "parser.h" // lalr1.cc:392
 
 
 
 
 
 
@@ -294,11 +295,18 @@ namespace isc { namespace eval {
     /// An auxiliary type to compute the largest semantic type.
     /// An auxiliary type to compute the largest semantic type.
     union union_type
     union union_type
     {
     {
+      // option_repr_type
+      char dummy1[sizeof(TokenOption::RepresentationType)];
+
       // "constant string"
       // "constant string"
       // "integer"
       // "integer"
       // "constant hexstring"
       // "constant hexstring"
+      // "option name"
       // TOKEN
       // TOKEN
-      char dummy1[sizeof(std::string)];
+      char dummy2[sizeof(std::string)];
+
+      // option_code
+      char dummy3[sizeof(uint16_t)];
 };
 };
 
 
     /// Symbol semantic values.
     /// Symbol semantic values.
@@ -337,7 +345,8 @@ namespace isc { namespace eval {
         TOKEN_STRING = 270,
         TOKEN_STRING = 270,
         TOKEN_INTEGER = 271,
         TOKEN_INTEGER = 271,
         TOKEN_HEXSTRING = 272,
         TOKEN_HEXSTRING = 272,
-        TOKEN_TOKEN = 273
+        TOKEN_OPTION_NAME = 273,
+        TOKEN_TOKEN = 274
       };
       };
     };
     };
 
 
@@ -375,8 +384,12 @@ namespace isc { namespace eval {
 
 
   basic_symbol (typename Base::kind_type t, const location_type& l);
   basic_symbol (typename Base::kind_type t, const location_type& l);
 
 
+  basic_symbol (typename Base::kind_type t, const TokenOption::RepresentationType v, const location_type& l);
+
   basic_symbol (typename Base::kind_type t, const std::string v, const location_type& l);
   basic_symbol (typename Base::kind_type t, const std::string v, const location_type& l);
 
 
+  basic_symbol (typename Base::kind_type t, const uint16_t v, const location_type& l);
+
 
 
       /// Constructor for symbols with semantic value.
       /// Constructor for symbols with semantic value.
       basic_symbol (typename Base::kind_type t,
       basic_symbol (typename Base::kind_type t,
@@ -510,6 +523,10 @@ namespace isc { namespace eval {
 
 
     static inline
     static inline
     symbol_type
     symbol_type
+    make_OPTION_NAME (const std::string& v, const location_type& l);
+
+    static inline
+    symbol_type
     make_TOKEN (const std::string& v, const location_type& l);
     make_TOKEN (const std::string& v, const location_type& l);
 
 
 
 
@@ -597,7 +614,7 @@ namespace isc { namespace eval {
   // number is the opposite.  If YYTABLE_NINF, syntax error.
   // number is the opposite.  If YYTABLE_NINF, syntax error.
   static const unsigned char yytable_[];
   static const unsigned char yytable_[];
 
 
-  static const unsigned char yycheck_[];
+  static const signed char yycheck_[];
 
 
   // YYSTOS[STATE-NUM] -- The (internal number of the) accessing
   // YYSTOS[STATE-NUM] -- The (internal number of the) accessing
   // symbol of state STATE-NUM.
   // symbol of state STATE-NUM.
@@ -717,12 +734,12 @@ namespace isc { namespace eval {
     enum
     enum
     {
     {
       yyeof_ = 0,
       yyeof_ = 0,
-      yylast_ = 21,     ///< Last index in yytable_.
-      yynnts_ = 6,  ///< Number of nonterminal symbols.
+      yylast_ = 24,     ///< Last index in yytable_.
+      yynnts_ = 8,  ///< Number of nonterminal symbols.
       yyfinal_ = 11, ///< Termination state number.
       yyfinal_ = 11, ///< Termination state number.
       yyterror_ = 1,
       yyterror_ = 1,
       yyerrcode_ = 256,
       yyerrcode_ = 256,
-      yyntokens_ = 19  ///< Number of tokens.
+      yyntokens_ = 20  ///< Number of tokens.
     };
     };
 
 
 
 
@@ -766,9 +783,9 @@ namespace isc { namespace eval {
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
        2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
        5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
        5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
-      15,    16,    17,    18
+      15,    16,    17,    18,    19
     };
     };
-    const unsigned int user_token_number_max_ = 273;
+    const unsigned int user_token_number_max_ = 274;
     const token_number_type undef_token_ = 2;
     const token_number_type undef_token_ = 2;
 
 
     if (static_cast<int>(t) <= yyeof_)
     if (static_cast<int>(t) <= yyeof_)
@@ -801,13 +818,22 @@ namespace isc { namespace eval {
   {
   {
       switch (other.type_get ())
       switch (other.type_get ())
     {
     {
+      case 25: // option_repr_type
+        value.copy< TokenOption::RepresentationType > (other.value);
+        break;
+
       case 15: // "constant string"
       case 15: // "constant string"
       case 16: // "integer"
       case 16: // "integer"
       case 17: // "constant hexstring"
       case 17: // "constant hexstring"
-      case 18: // TOKEN
+      case 18: // "option name"
+      case 19: // TOKEN
         value.copy< std::string > (other.value);
         value.copy< std::string > (other.value);
         break;
         break;
 
 
+      case 24: // option_code
+        value.copy< uint16_t > (other.value);
+        break;
+
       default:
       default:
         break;
         break;
     }
     }
@@ -825,13 +851,22 @@ namespace isc { namespace eval {
     (void) v;
     (void) v;
       switch (this->type_get ())
       switch (this->type_get ())
     {
     {
+      case 25: // option_repr_type
+        value.copy< TokenOption::RepresentationType > (v);
+        break;
+
       case 15: // "constant string"
       case 15: // "constant string"
       case 16: // "integer"
       case 16: // "integer"
       case 17: // "constant hexstring"
       case 17: // "constant hexstring"
-      case 18: // TOKEN
+      case 18: // "option name"
+      case 19: // TOKEN
         value.copy< std::string > (v);
         value.copy< std::string > (v);
         break;
         break;
 
 
+      case 24: // option_code
+        value.copy< uint16_t > (v);
+        break;
+
       default:
       default:
         break;
         break;
     }
     }
@@ -848,12 +883,26 @@ namespace isc { namespace eval {
   {}
   {}
 
 
   template <typename Base>
   template <typename Base>
+  EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const TokenOption::RepresentationType v, const location_type& l)
+    : Base (t)
+    , value (v)
+    , location (l)
+  {}
+
+  template <typename Base>
   EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const std::string v, const location_type& l)
   EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const std::string v, const location_type& l)
     : Base (t)
     : Base (t)
     , value (v)
     , value (v)
     , location (l)
     , location (l)
   {}
   {}
 
 
+  template <typename Base>
+  EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const uint16_t v, const location_type& l)
+    : Base (t)
+    , value (v)
+    , location (l)
+  {}
+
 
 
   template <typename Base>
   template <typename Base>
   inline
   inline
@@ -880,13 +929,22 @@ namespace isc { namespace eval {
     // Type destructor.
     // Type destructor.
     switch (yytype)
     switch (yytype)
     {
     {
+      case 25: // option_repr_type
+        value.template destroy< TokenOption::RepresentationType > ();
+        break;
+
       case 15: // "constant string"
       case 15: // "constant string"
       case 16: // "integer"
       case 16: // "integer"
       case 17: // "constant hexstring"
       case 17: // "constant hexstring"
-      case 18: // TOKEN
+      case 18: // "option name"
+      case 19: // TOKEN
         value.template destroy< std::string > ();
         value.template destroy< std::string > ();
         break;
         break;
 
 
+      case 24: // option_code
+        value.template destroy< uint16_t > ();
+        break;
+
       default:
       default:
         break;
         break;
     }
     }
@@ -910,13 +968,22 @@ namespace isc { namespace eval {
     super_type::move(s);
     super_type::move(s);
       switch (this->type_get ())
       switch (this->type_get ())
     {
     {
+      case 25: // option_repr_type
+        value.move< TokenOption::RepresentationType > (s.value);
+        break;
+
       case 15: // "constant string"
       case 15: // "constant string"
       case 16: // "integer"
       case 16: // "integer"
       case 17: // "constant hexstring"
       case 17: // "constant hexstring"
-      case 18: // TOKEN
+      case 18: // "option name"
+      case 19: // TOKEN
         value.move< std::string > (s.value);
         value.move< std::string > (s.value);
         break;
         break;
 
 
+      case 24: // option_code
+        value.move< uint16_t > (s.value);
+        break;
+
       default:
       default:
         break;
         break;
     }
     }
@@ -973,7 +1040,7 @@ namespace isc { namespace eval {
     yytoken_number_[] =
     yytoken_number_[] =
     {
     {
        0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
        0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
-     265,   266,   267,   268,   269,   270,   271,   272,   273
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274
     };
     };
     return static_cast<token_type> (yytoken_number_[type]);
     return static_cast<token_type> (yytoken_number_[type]);
   }
   }
@@ -1075,6 +1142,12 @@ namespace isc { namespace eval {
   }
   }
 
 
   EvalParser::symbol_type
   EvalParser::symbol_type
+  EvalParser::make_OPTION_NAME (const std::string& v, const location_type& l)
+  {
+    return symbol_type (token::TOKEN_OPTION_NAME, v, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_TOKEN (const std::string& v, const location_type& l)
   EvalParser::make_TOKEN (const std::string& v, const location_type& l)
   {
   {
     return symbol_type (token::TOKEN_TOKEN, v, l);
     return symbol_type (token::TOKEN_TOKEN, v, l);
@@ -1083,7 +1156,7 @@ namespace isc { namespace eval {
 
 
 #line 21 "parser.yy" // lalr1.cc:392
 #line 21 "parser.yy" // lalr1.cc:392
 } } // isc::eval
 } } // isc::eval
-#line 1087 "parser.h" // lalr1.cc:392
+#line 1160 "parser.h" // lalr1.cc:392
 
 
 
 
 
 

+ 28 - 37
src/lib/eval/parser.yy

@@ -25,6 +25,7 @@
 #include <string>
 #include <string>
 #include <eval/token.h>
 #include <eval/token.h>
 #include <eval/eval_context_decl.h>
 #include <eval/eval_context_decl.h>
+#include <dhcp/option.h>
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
 
 
 using namespace isc::dhcp;
 using namespace isc::dhcp;
@@ -39,6 +40,7 @@ using namespace isc::eval;
 {
 {
 # include "eval_context.h"
 # include "eval_context.h"
 }
 }
+
 %define api.token.prefix {TOKEN_}
 %define api.token.prefix {TOKEN_}
 %token
 %token
   END  0  "end of file"
   END  0  "end of file"
@@ -59,37 +61,13 @@ using namespace isc::eval;
 %token <std::string> STRING "constant string"
 %token <std::string> STRING "constant string"
 %token <std::string> INTEGER "integer"
 %token <std::string> INTEGER "integer"
 %token <std::string> HEXSTRING "constant hexstring"
 %token <std::string> HEXSTRING "constant hexstring"
+%token <std::string> OPTION_NAME "option name"
 %token <std::string> TOKEN
 %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));
-}
-}
+%type <uint16_t> option_code
+%type <TokenOption::RepresentationType> option_repr_type
 
 
-}
+%printer { yyoutput << $$; } <*>;
 
 
 %%
 %%
 
 
@@ -118,16 +96,9 @@ string_expr : STRING
                       TokenPtr hex(new TokenHexString($1));
                       TokenPtr hex(new TokenHexString($1));
                       ctx.expression.push_back(hex);
                       ctx.expression.push_back(hex);
                   }
                   }
-            | OPTION "[" INTEGER "]" DOT TEXT
+            | OPTION "[" option_code "]" "." option_repr_type
                   {
                   {
-                      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
-                  {
-                      uint16_t numeric_code = convert_option_code($3, @3, ctx);
-                      TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL));
+                      TokenPtr opt(new TokenOption($3, $6));
                       ctx.expression.push_back(opt);
                       ctx.expression.push_back(opt);
                   }
                   }
             | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
             | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
@@ -139,6 +110,26 @@ string_expr : STRING
                 // Temporary unused token to avoid explict but long errors
                 // Temporary unused token to avoid explict but long errors
             ;
             ;
 
 
+option_code : INTEGER
+                 {
+                     $$ = ctx.convertOptionCode($1, @1);
+                 }
+            | OPTION_NAME
+                 {
+                     $$ = ctx.convertOptionName($1, @1);
+                 }
+            ;
+
+option_repr_type : TEXT
+                      {
+                          $$ = TokenOption::TEXTUAL;
+                      }
+                 | HEX
+                      {
+                          $$ = TokenOption::HEXADECIMAL;
+                      }
+                 ;
+
 start_expr : INTEGER
 start_expr : INTEGER
                  {
                  {
                      TokenPtr str(new TokenString($1));
                      TokenPtr str(new TokenString($1));

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

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

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

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

+ 77 - 14
src/lib/eval/tests/context_unittest.cc

@@ -16,6 +16,7 @@
 #include <eval/token.h>
 #include <eval/token.h>
 #include <eval/eval_context.h>
 #include <eval/eval_context.h>
 #include <eval/token.h>
 #include <eval/token.h>
+#include <dhcp/option.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt4.h>
 
 
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
@@ -30,6 +31,11 @@ namespace {
 /// @brief Test class for testing EvalContext aka class test parsing
 /// @brief Test class for testing EvalContext aka class test parsing
 class EvalContextTest : public ::testing::Test {
 class EvalContextTest : public ::testing::Test {
 public:
 public:
+    /// @brief constructor to initialize members
+    EvalContextTest() : ::testing::Test(),
+    universe_(Option::V4), parsed_(false)
+    { }
+
     /// @brief checks if the given token is a string with the expected value
     /// @brief checks if the given token is a string with the expected value
     void checkTokenString(const TokenPtr& token, const std::string& expected) {
     void checkTokenString(const TokenPtr& token, const std::string& expected) {
         ASSERT_TRUE(token);
         ASSERT_TRUE(token);
@@ -94,7 +100,7 @@ public:
     /// @brief checks if the given expression raises the expected message
     /// @brief checks if the given expression raises the expected message
     /// when it is parsed.
     /// when it is parsed.
     void checkError(const string& expr, const string& msg) {
     void checkError(const string& expr, const string& msg) {
-        EvalContext eval;
+        EvalContext eval(universe_);
         parsed_ = false;
         parsed_ = false;
         try {
         try {
             parsed_ = eval.parseString(expr);
             parsed_ = eval.parseString(expr);
@@ -109,21 +115,28 @@ public:
         }
         }
     }
     }
 
 
+    /// @brief sets the universe
+    /// @note the default universe is DHCPv4
+    void setUniverse(const Option::Universe& universe) {
+        universe_ = universe;
+    }
+
+    Option::Universe universe_;
     bool parsed_; ///< Parsing status
     bool parsed_; ///< Parsing status
 };
 };
 
 
 // Test the parsing of a basic expression
 // Test the parsing of a basic expression
 TEST_F(EvalContextTest, basic) {
 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_);
     EXPECT_TRUE(parsed_);
 }
 }
 
 
 // Test the parsing of a string terminal
 // Test the parsing of a string terminal
 TEST_F(EvalContextTest, string) {
 TEST_F(EvalContextTest, string) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
     EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
     EXPECT_TRUE(parsed_);
     EXPECT_TRUE(parsed_);
@@ -140,7 +153,7 @@ TEST_F(EvalContextTest, string) {
 // Test the parsing of a basic expression using integers
 // Test the parsing of a basic expression using integers
 TEST_F(EvalContextTest, integer) {
 TEST_F(EvalContextTest, integer) {
 
 
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
 
     EXPECT_NO_THROW(parsed_ =
     EXPECT_NO_THROW(parsed_ =
         eval.parseString("substring(option[123].text, 0, 2) == '42'"));
         eval.parseString("substring(option[123].text, 0, 2) == '42'"));
@@ -149,7 +162,7 @@ TEST_F(EvalContextTest, integer) {
 
 
 // Test the parsing of a hexstring terminal
 // Test the parsing of a hexstring terminal
 TEST_F(EvalContextTest, hexstring) {
 TEST_F(EvalContextTest, hexstring) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("0x666f6f == 'foo'"));
     EXPECT_NO_THROW(parsed_ = eval.parseString("0x666f6f == 'foo'"));
     EXPECT_TRUE(parsed_);
     EXPECT_TRUE(parsed_);
@@ -164,7 +177,7 @@ TEST_F(EvalContextTest, hexstring) {
 // Test the parsing of a hexstring terminal with an odd number of
 // Test the parsing of a hexstring terminal with an odd number of
 // hexadecimal digits
 // hexadecimal digits
 TEST_F(EvalContextTest, oddHexstring) {
 TEST_F(EvalContextTest, oddHexstring) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("0X7 == 'foo'"));
     EXPECT_NO_THROW(parsed_ = eval.parseString("0X7 == 'foo'"));
     EXPECT_TRUE(parsed_);
     EXPECT_TRUE(parsed_);
@@ -178,7 +191,7 @@ TEST_F(EvalContextTest, oddHexstring) {
 
 
 // Test the parsing of an equal expression
 // Test the parsing of an equal expression
 TEST_F(EvalContextTest, equal) {
 TEST_F(EvalContextTest, equal) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
     EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
     EXPECT_TRUE(parsed_);
     EXPECT_TRUE(parsed_);
@@ -196,7 +209,7 @@ TEST_F(EvalContextTest, equal) {
 
 
 // Test the parsing of an option terminal
 // Test the parsing of an option terminal
 TEST_F(EvalContextTest, option) {
 TEST_F(EvalContextTest, option) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'foo'"));
     EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'foo'"));
     EXPECT_TRUE(parsed_);
     EXPECT_TRUE(parsed_);
@@ -204,9 +217,43 @@ TEST_F(EvalContextTest, option) {
     checkTokenOption(eval.expression.at(0), 123);
     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 checking that whitespace can surround option name.
+TEST_F(EvalContextTest, optionWithNameAndWhitespace) {
+    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 checking that newlines can surround option name.
+TEST_F(EvalContextTest, optionWithNameAndNewline) {
+    EvalContext eval(Option::V4);
+
+    // Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++.
+    EXPECT_NO_THROW(parsed_ =
+        eval.parseString("option[\n host-name \n ].text == \n'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 parsing of an option represented as hexadecimal string.
 TEST_F(EvalContextTest, optionHex) {
 TEST_F(EvalContextTest, optionHex) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
 
     EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].hex == 0x666F6F"));
     EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].hex == 0x666F6F"));
     EXPECT_TRUE(parsed_);
     EXPECT_TRUE(parsed_);
@@ -216,7 +263,7 @@ TEST_F(EvalContextTest, optionHex) {
 
 
 // Test the parsing of a substring expression
 // Test the parsing of a substring expression
 TEST_F(EvalContextTest, substring) {
 TEST_F(EvalContextTest, substring) {
-    EvalContext eval;
+    EvalContext eval(Option::V4);
 
 
     EXPECT_NO_THROW(parsed_ =
     EXPECT_NO_THROW(parsed_ =
         eval.parseString("substring('foobar',2,all) == 'obar'"));
         eval.parseString("substring('foobar',2,all) == 'obar'"));
@@ -257,10 +304,15 @@ TEST_F(EvalContextTest, scanParseErrors) {
     checkError("===", "<string>:1.1-2: syntax error, unexpected ==");
     checkError("===", "<string>:1.1-2: syntax error, unexpected ==");
     checkError("option[-1].text",
     checkError("option[-1].text",
                "<string>:1.8-9: Option code has invalid "
                "<string>:1.8-9: Option code has invalid "
-               "value in -1. Allowed range: 0..65535");
+               "value in -1. Allowed range: 0..255");
+    checkError("option[256].text",
+               "<string>:1.8-10: Option code has invalid "
+               "value in 256. Allowed range: 0..255");
+    setUniverse(Option::V6);
     checkError("option[65536].text",
     checkError("option[65536].text",
                "<string>:1.8-12: Option code has invalid "
                "<string>:1.8-12: Option code has invalid "
                "value in 65536. Allowed range: 0..65535");
                "value in 65536. Allowed range: 0..65535");
+    setUniverse(Option::V4);
     checkError("option[12345678901234567890].text",
     checkError("option[12345678901234567890].text",
                "<string>:1.8-27: Failed to convert 12345678901234567890 "
                "<string>:1.8-27: Failed to convert 12345678901234567890 "
                "to an integer.");
                "to an integer.");
@@ -269,6 +321,13 @@ TEST_F(EvalContextTest, scanParseErrors) {
                " expecting .");
                " expecting .");
     checkError("option[123].text < 'foo'", "<string>:1.18: Invalid"
     checkError("option[123].text < 'foo'", "<string>:1.18: Invalid"
                " character: <");
                " character: <");
+    checkError("option[-ab].text", "<string>:1.8: Invalid character: -");
+    checkError("option[0ab].text",
+               "<string>:1.9-10: syntax error, unexpected option name, "
+               "expecting ]");
+    checkError("option[ab_].hex", "<string>:1.8: Invalid character: a");
+    checkError("option[\nhost-name\n].hex =\n= 'foo'",
+               "<string>:3.7: Invalid character: =");
     checkError("substring('foo',12345678901234567890,1)",
     checkError("substring('foo',12345678901234567890,1)",
                "<string>:1.17-36: Failed to convert 12345678901234567890 "
                "<string>:1.17-36: Failed to convert 12345678901234567890 "
                "to an integer.");
                "to an integer.");
@@ -291,11 +350,15 @@ TEST_F(EvalContextTest, parseErrors) {
     checkError("option['ab'].text == 'foo'",
     checkError("option['ab'].text == 'foo'",
                "<string>:1.8-11: syntax error, "
                "<string>:1.8-11: syntax error, "
                "unexpected constant string, "
                "unexpected constant string, "
-               "expecting integer");
+               "expecting integer or option name");
+    checkError("option[ab].text == 'foo'",
+               "<string>:1.8-9: option 'ab' is not defined");
     checkError("option[0xa].text == 'ab'",
     checkError("option[0xa].text == 'ab'",
                "<string>:1.8-10: syntax error, "
                "<string>:1.8-10: syntax error, "
                "unexpected constant hexstring, "
                "unexpected constant hexstring, "
-               "expecting integer");
+               "expecting integer or option name");
+    checkError("option[10].bin", "<string>:1.12: Invalid character: b");
+    checkError("option[boot-size].bin", "<string>:1.19: Invalid character: b");
     checkError("substring('foobar') == 'f'",
     checkError("substring('foobar') == 'f'",
                "<string>:1.19: syntax error, "
                "<string>:1.19: syntax error, "
                "unexpected ), expecting \",\"");
                "unexpected ), expecting \",\"");

+ 1 - 1
src/lib/eval/tests/evaluate_unittest.cc

@@ -217,7 +217,7 @@ TEST_F(EvaluateTest, optionHex) {
 
 
     ASSERT_NO_THROW(toption.reset(new TokenOption(100, TokenOption::HEXADECIMAL)));
     ASSERT_NO_THROW(toption.reset(new TokenOption(100, TokenOption::HEXADECIMAL)));
     e_.push_back(toption);
     e_.push_back(toption);
-    ASSERT_NO_THROW(tstring.reset(new TokenString("0x68756E6472656434")));
+    ASSERT_NO_THROW(tstring.reset(new TokenString("hundred4")));
     e_.push_back(tstring);
     e_.push_back(tstring);
     ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
     ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
     e_.push_back(tequal);
     e_.push_back(tequal);

+ 2 - 2
src/lib/eval/tests/token_unittest.cc

@@ -300,7 +300,7 @@ TEST_F(TokenTest, optionHexString4) {
     values_.pop();
     values_.pop();
 
 
     // Then the content of the option 100.
     // Then the content of the option 100.
-    EXPECT_EQ("0x68756E6472656434", values_.top());
+    EXPECT_EQ("hundred4", values_.top());
 }
 }
 
 
 // This test checks if a token representing an option value is able to extract
 // This test checks if a token representing an option value is able to extract
@@ -357,7 +357,7 @@ TEST_F(TokenTest, optionHexString6) {
     values_.pop();
     values_.pop();
 
 
     // Then the content of the option 100.
     // Then the content of the option 100.
-    EXPECT_EQ("0x68756E6472656436", values_.top());
+    EXPECT_EQ("hundred6", values_.top());
 }
 }
 
 
 // This test checks if a token representing an == operator is able to
 // This test checks if a token representing an == operator is able to

+ 14 - 5
src/lib/eval/token.cc

@@ -65,13 +65,22 @@ TokenHexString::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
 void
 void
 TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
 TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
     OptionPtr opt = pkt.getOption(option_code_);
     OptionPtr opt = pkt.getOption(option_code_);
+    std::string opt_str;
     if (opt) {
     if (opt) {
-        values.push(representation_type_ == TEXTUAL ? opt->toString()
-                    : opt->toHexString());
-    } else {
-        // Option not found, push empty string
-        values.push("");
+        if (representation_type_ == TEXTUAL) {
+            opt_str = opt->toString();
+        } else {
+            std::vector<uint8_t> binary = opt->toBinary();
+            opt_str.resize(binary.size());
+            if (!binary.empty()) {
+                memmove(&opt_str[0], &binary[0], binary.size());
+            }
+        }
     }
     }
+
+    // Push value of the option or empty string if there was no such option
+    // in the packet.
+    values.push(opt_str);
 }
 }
 
 
 void
 void

+ 1 - 0
src/lib/util/Makefile.am

@@ -21,6 +21,7 @@ libkea_util_la_SOURCES += pointer_util.h
 libkea_util_la_SOURCES += process_spawn.h process_spawn.cc
 libkea_util_la_SOURCES += process_spawn.h process_spawn.cc
 libkea_util_la_SOURCES += range_utilities.h
 libkea_util_la_SOURCES += range_utilities.h
 libkea_util_la_SOURCES += signal_set.cc signal_set.h
 libkea_util_la_SOURCES += signal_set.cc signal_set.h
+libkea_util_la_SOURCES += staged_value.h
 libkea_util_la_SOURCES += stopwatch.cc stopwatch.h
 libkea_util_la_SOURCES += stopwatch.cc stopwatch.h
 libkea_util_la_SOURCES += stopwatch_impl.cc stopwatch_impl.h
 libkea_util_la_SOURCES += stopwatch_impl.cc stopwatch_impl.h
 libkea_util_la_SOURCES += versioned_csv_file.h versioned_csv_file.cc
 libkea_util_la_SOURCES += versioned_csv_file.h versioned_csv_file.cc

+ 126 - 0
src/lib/util/staged_value.h

@@ -0,0 +1,126 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef STAGED_VALUE_H
+#define STAGED_VALUE_H
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace util {
+
+/// @brief This class implements set/commit mechanism for a single object.
+///
+/// In some cases it is desired to set value for an object while keeping
+/// ability to revert to an original value under certain conditions.
+/// This is often desired for objects holding some part of application's
+/// configuration. Configuration is usually a multi-step process and
+/// may fail on almost any stage. If this happens, the last good
+/// configuration should be used. This implies that some of the state
+/// of some of the objects needs to be reverted.
+///
+/// This class implements a mechanism for setting and committing a value.
+/// Until the new value has been committed it is possible to revert to
+/// an original value.
+///
+/// @tparam ValueType Type of the value represented by this class.
+template<typename ValueType>
+class StagedValue : public boost::noncopyable {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Initializes the default value.
+    StagedValue()
+        : staging_(new ValueType()), current_(new ValueType()),
+          modified_(false) {
+    }
+
+    /// @brief Retrieves current value.
+    ///
+    /// If the value hasn't been modified since last commit, reset or
+    /// revert operation, a committed value is returned. If the value
+    /// has been modified, the modified value is returned.
+    const ValueType& getValue() const {
+        return (modified_ ? *staging_ : *current_);
+    }
+
+    /// @brief Sets new value.
+    ///
+    /// @param new_value New value to be assigned.
+    void setValue(const ValueType& new_value) {
+        *staging_ = new_value;
+        modified_ = true;
+    }
+
+    /// @brief Commits a value.
+    void commit() {
+        // Only apply changes if any modifications made.
+        if (modified_) {
+            current_ = staging_;
+        }
+        revert();
+    }
+
+    /// @brief Resets value to defaults.
+    void reset() {
+        revert();
+        current_.reset(new ValueType());
+    }
+
+    /// @brief Reverts any modifications since last commit.
+    void revert() {
+        staging_.reset(new ValueType());
+        modified_ = false;
+    }
+
+    /// @brief Assignment operator.
+    ///
+    /// @param value New value to be assigned.
+    /// @return Reference to this.
+    StagedValue& operator=(const ValueType& value) {
+        setValue(value);
+        return (*this);
+    }
+
+    /// @brief Conversion operator to value type.
+    ///
+    /// @return Reference to value represented by this object.
+    operator const ValueType&() const {
+        return (getValue());
+    }
+
+private:
+
+    /// @brief Pointer to staging value.
+    ///
+    /// This value holds any modifications made.
+    boost::shared_ptr<ValueType> staging_;
+
+    /// @brief Pointer to committed value.
+    ///
+    /// This value holds last committed changes.
+    boost::shared_ptr<ValueType> current_;
+
+    /// @brief Boolean flag which indicates if any modifications have been
+    /// applied since last commit.
+    bool modified_;
+
+};
+
+} // namespace isc::util
+} // namespace isc
+
+#endif // STAGED_VALUE_H

+ 1 - 0
src/lib/util/tests/Makefile.am

@@ -45,6 +45,7 @@ run_unittests_SOURCES += process_spawn_unittest.cc
 run_unittests_SOURCES += qid_gen_unittest.cc
 run_unittests_SOURCES += qid_gen_unittest.cc
 run_unittests_SOURCES += random_number_generator_unittest.cc
 run_unittests_SOURCES += random_number_generator_unittest.cc
 run_unittests_SOURCES += socketsession_unittest.cc
 run_unittests_SOURCES += socketsession_unittest.cc
+run_unittests_SOURCES += staged_value_unittest.cc
 run_unittests_SOURCES += strutil_unittest.cc
 run_unittests_SOURCES += strutil_unittest.cc
 run_unittests_SOURCES += time_utilities_unittest.cc
 run_unittests_SOURCES += time_utilities_unittest.cc
 run_unittests_SOURCES += range_utilities_unittest.cc
 run_unittests_SOURCES += range_utilities_unittest.cc

+ 114 - 0
src/lib/util/tests/staged_value_unittest.cc

@@ -0,0 +1,114 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <util/staged_value.h>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+namespace {
+
+using namespace isc::util;
+
+// This test verifies that the value can be assigned and committed.
+TEST(StagedValueTest, assignAndCommit) {
+    // Initally the value should be set to a default
+    StagedValue<int> value;
+    ASSERT_EQ(0, value.getValue());
+
+    // Set the new value without committing it and make sure it
+    // can be retrieved.
+    value.setValue(4);
+    ASSERT_EQ(4, value.getValue());
+
+    // Commit the value and make sure it still can be retrieved.
+    value.commit();
+    ASSERT_EQ(4, value.getValue());
+
+    // Set new value and retrieve it.
+    value.setValue(10);
+    ASSERT_EQ(10, value.getValue());
+
+    // Do it again and commit it.
+    value.setValue(20);
+    ASSERT_EQ(20, value.getValue());
+    value.commit();
+    EXPECT_EQ(20, value.getValue());
+}
+
+// This test verifies that the value can be reverted if it hasn't been
+// committed.
+TEST(StagedValueTest, revert) {
+    // Set the value and commit.
+    StagedValue<int> value;
+    value.setValue(123);
+    value.commit();
+
+    // Set new value and do not commit.
+    value.setValue(500);
+    // The new value should be the one returned.
+    ASSERT_EQ(500, value.getValue());
+    // But, reverting gets us back to original value.
+    value.revert();
+    EXPECT_EQ(123, value.getValue());
+    // Reverting again doesn't have any effect.
+    value.revert();
+    EXPECT_EQ(123, value.getValue());
+}
+
+// This test verifies that the value can be restored to an original one.
+TEST(StagedValueTest, reset) {
+    // Set the new value and commit.
+    StagedValue<int> value;
+    value.setValue(123);
+    value.commit();
+
+    // Override the value but do not commit.
+    value.setValue(500);
+
+    // Resetting should take us back to default value.
+    value.reset();
+    EXPECT_EQ(0, value.getValue());
+    value.revert();
+    EXPECT_EQ(0, value.getValue());
+}
+
+// This test verifies that second commit doesn't modify a value.
+TEST(StagedValueTest, commit) {
+    // Set the value and commit.
+    StagedValue<int> value;
+    value.setValue(123);
+    value.commit();
+
+    // Second commit should have no effect.
+    value.commit();
+    EXPECT_EQ(123, value.getValue());
+}
+
+// This test checks that type conversion operator works correctly.
+TEST(StagedValueTest, conversionOperator) {
+    StagedValue<int> value;
+    value.setValue(244);
+    EXPECT_EQ(244, value);
+}
+
+// This test checks that the assignment operator works correctly.
+TEST(StagedValueTest, assignmentOperator) {
+    StagedValue<int> value;
+    value = 111;
+    EXPECT_EQ(111, value);
+}
+
+
+} // end of anonymous namespace