Commit 77e78881 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

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

parent 6011b532
......@@ -248,7 +248,6 @@ LibDHCP::clearRuntimeOptionDefs() {
runtime_option_defs_.clearItems();
}
bool
LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
if (u == Option::V6) {
......
......@@ -121,7 +121,6 @@ public:
static OptionDefContainerPtr
getRuntimeOptionDefs(const std::string& space);
/// @brief Check if the specified option is a standard option.
///
/// @param u universe (V4 or V6)
......
......@@ -50,7 +50,7 @@ ExpressionParser::build(ConstElementPtr expression_cfg) {
std::string value;
expression_cfg->getValue(value);
try {
EvalContext eval_ctx;
EvalContext eval_ctx(global_context_->universe_);
eval_ctx.parseString(value);
local_expression_.reset(new Expression());
*local_expression_ = eval_ctx.expression;
......
......@@ -15,10 +15,12 @@
#include <eval/eval_context.h>
#include <eval/parser.h>
#include <exceptions/exceptions.h>
#include <dhcp/option.h>
#include <fstream>
EvalContext::EvalContext()
: trace_scanning_(false), trace_parsing_(false)
EvalContext::EvalContext(const Option::Universe& option_universe)
: trace_scanning_(false), trace_parsing_(false),
option_universe_(option_universe)
{
}
......@@ -32,7 +34,7 @@ EvalContext::parseString(const std::string& str)
file_ = "<string>";
string_ = str;
scanStringBegin();
isc::eval::EvalParser parser(*this);
isc::eval::EvalParser parser(*this, option_universe_);
parser.set_debug_level(trace_parsing_);
int res = parser.parse();
scanStringEnd();
......
......@@ -42,7 +42,11 @@ class EvalContext
{
public:
/// @brief Default constructor.
EvalContext();
///
/// @param option_universe Option universe: DHCPv4 or DHCPv6. This is used
/// by the parser to determine which option definitions set should be used
/// to map option names to option codes.
EvalContext(const Option::Universe& option_universe);
/// @brief destructor
virtual ~EvalContext();
......@@ -55,7 +59,7 @@ public:
/// @brief Method called after the last tokens are scanned from a string.
void scanStringEnd();
/// @brief Run the parser on the string specified.
///
/// @param str string to be written
......@@ -87,7 +91,12 @@ public:
/// @brief Flag determing parser debugging.
bool trace_parsing_;
/// @brief Option universe: DHCPv4 or DHCPv6.
///
/// This is used by the parser to determine which option definitions
/// set should be used to map option name to option code.
Option::Universe option_universe_;
};
}; // end of isc::eval namespace
......
......@@ -49,10 +49,10 @@
#line 51 "parser.cc" // lalr1.cc:412
// Unqualified %code blocks.
#line 39 "parser.yy" // lalr1.cc:413
#line 40 "parser.yy" // lalr1.cc:413
# include "eval_context.h"
#line 67 "parser.yy" // lalr1.cc:413
#line 72 "parser.yy" // lalr1.cc:413
namespace {
......@@ -206,13 +206,14 @@ namespace isc { namespace eval {
/// Build a parser object.
EvalParser::EvalParser (EvalContext& ctx_yyarg)
EvalParser::EvalParser (EvalContext& ctx_yyarg, const Option::Universe& option_universe_yyarg)
:
#if YYDEBUG
yydebug_ (false),
yycdebug_ (&std::cerr),
#endif
ctx (ctx_yyarg)
ctx (ctx_yyarg),
option_universe (option_universe_yyarg)
{}
EvalParser::~EvalParser ()
......@@ -344,30 +345,30 @@ namespace isc { namespace eval {
{
case 15: // "constant string"
#line 64 "parser.yy" // lalr1.cc:636
#line 69 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 350 "parser.cc" // lalr1.cc:636
#line 351 "parser.cc" // lalr1.cc:636
break;
case 16: // "integer"
#line 64 "parser.yy" // lalr1.cc:636
#line 69 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 357 "parser.cc" // lalr1.cc:636
#line 358 "parser.cc" // lalr1.cc:636
break;
case 17: // "constant hexstring"
#line 64 "parser.yy" // lalr1.cc:636
#line 69 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 364 "parser.cc" // lalr1.cc:636
#line 365 "parser.cc" // lalr1.cc:636
break;
case 18: // TOKEN
#line 64 "parser.yy" // lalr1.cc:636
#line 69 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 371 "parser.cc" // lalr1.cc:636
#line 372 "parser.cc" // lalr1.cc:636
break;
......@@ -592,90 +593,124 @@ namespace isc { namespace eval {
switch (yyn)
{
case 3:
#line 105 "parser.yy" // lalr1.cc:859
#line 110 "parser.yy" // lalr1.cc:859
{
TokenPtr eq(new TokenEqual());
ctx.expression.push_back(eq);
}
#line 601 "parser.cc" // lalr1.cc:859
#line 602 "parser.cc" // lalr1.cc:859
break;
case 4:
#line 112 "parser.yy" // lalr1.cc:859
#line 117 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
#line 610 "parser.cc" // lalr1.cc:859
#line 611 "parser.cc" // lalr1.cc:859
break;
case 5:
#line 117 "parser.yy" // lalr1.cc:859
#line 122 "parser.yy" // lalr1.cc:859
{
TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(hex);
}
#line 619 "parser.cc" // lalr1.cc:859
#line 620 "parser.cc" // lalr1.cc:859
break;
case 6:
#line 122 "parser.yy" // lalr1.cc:859
#line 127 "parser.yy" // lalr1.cc:859
{
uint16_t numeric_code = convert_option_code(yystack_[3].value.as< std::string > (), yystack_[3].location, ctx);
TokenPtr opt(new TokenOption(numeric_code, TokenOption::TEXTUAL));
ctx.expression.push_back(opt);
}
#line 629 "parser.cc" // lalr1.cc:859
#line 630 "parser.cc" // lalr1.cc:859
break;
case 7:
#line 128 "parser.yy" // lalr1.cc:859
#line 133 "parser.yy" // lalr1.cc:859
{
uint16_t numeric_code = convert_option_code(yystack_[3].value.as< std::string > (), yystack_[3].location, ctx);
TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL));
ctx.expression.push_back(opt);
}
#line 639 "parser.cc" // lalr1.cc:859
#line 640 "parser.cc" // lalr1.cc:859
break;
case 8:
#line 134 "parser.yy" // lalr1.cc:859
#line 139 "parser.yy" // lalr1.cc:859
{
try {
// This may result in exception if the specified
// name is unknown.
TokenPtr opt(new TokenOption(yystack_[3].value.as< std::string > (), option_universe,
TokenOption::TEXTUAL));
ctx.expression.push_back(opt);
} catch (const std::exception& ex) {
ctx.error(yystack_[3].location, ex.what());
}
}
#line 657 "parser.cc" // lalr1.cc:859
break;
case 9:
#line 152 "parser.yy" // lalr1.cc:859
{
try {
// This may result in exception if the specified
// name is unknown.
TokenPtr opt(new TokenOption(yystack_[3].value.as< std::string > (), option_universe,
TokenOption::HEXADECIMAL));
ctx.expression.push_back(opt);
} catch (const std::exception& ex) {
ctx.error(yystack_[3].location, ex.what());
}
}
#line 674 "parser.cc" // lalr1.cc:859
break;
case 10:
#line 165 "parser.yy" // lalr1.cc:859
{
TokenPtr sub(new TokenSubstring());
ctx.expression.push_back(sub);
}
#line 648 "parser.cc" // lalr1.cc:859
#line 683 "parser.cc" // lalr1.cc:859
break;
case 10:
#line 143 "parser.yy" // lalr1.cc:859
case 12:
#line 174 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
#line 657 "parser.cc" // lalr1.cc:859
#line 692 "parser.cc" // lalr1.cc:859
break;
case 11:
#line 150 "parser.yy" // lalr1.cc:859
case 13:
#line 181 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
#line 666 "parser.cc" // lalr1.cc:859
#line 701 "parser.cc" // lalr1.cc:859
break;
case 12:
#line 155 "parser.yy" // lalr1.cc:859
case 14:
#line 186 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString("all"));
ctx.expression.push_back(str);
}
#line 675 "parser.cc" // lalr1.cc:859
#line 710 "parser.cc" // lalr1.cc:859
break;
#line 679 "parser.cc" // lalr1.cc:859
#line 714 "parser.cc" // lalr1.cc:859
default:
break;
}
......@@ -937,17 +972,19 @@ namespace isc { namespace eval {
const signed char
EvalParser::yypact_[] =
{
-4, -9, -3, -10, -10, -10, 9, -10, 12, 1,
-4, -10, -4, -2, 6, -10, 10, 2, 0, -10,
11, -10, -10, -6, -10, -10, 8, -10
-4, -1, 6, -10, -10, -10, 4, -10, 15, -9,
-4, -10, -4, 5, 7, 10, -10, 13, 14, 8,
2, 9, -10, 16, -10, -10, -10, -10, -6, -10,
-10, 17, -10
};
const unsigned char
EvalParser::yydefact_[] =
{
0, 0, 0, 4, 5, 9, 0, 2, 0, 0,
0, 1, 0, 0, 0, 3, 0, 0, 0, 10,
0, 6, 7, 0, 12, 11, 0, 8
0, 0, 0, 4, 5, 11, 0, 2, 0, 0,
0, 1, 0, 0, 0, 0, 3, 0, 0, 0,
0, 0, 12, 0, 8, 9, 6, 7, 0, 14,
13, 0, 10
};
const signed char
......@@ -959,45 +996,46 @@ namespace isc { namespace eval {
const signed char
EvalParser::yydefgoto_[] =
{
-1, 6, 7, 8, 20, 26
-1, 6, 7, 8, 23, 31
};
const unsigned char
EvalParser::yytable_[] =
{
1, 2, 24, 14, 9, 15, 21, 22, 10, 11,
25, 3, 16, 4, 5, 12, 17, 13, 19, 18,
27, 23
1, 2, 29, 15, 11, 16, 13, 14, 24, 25,
30, 3, 9, 4, 5, 26, 27, 10, 12, 17,
19, 18, 20, 21, 22, 0, 28, 0, 0, 32
};
const unsigned char
const signed char
EvalParser::yycheck_[] =
{
4, 5, 8, 10, 13, 12, 6, 7, 11, 0,
16, 15, 14, 17, 18, 3, 10, 16, 16, 9,
12, 10
4, 5, 8, 10, 0, 12, 15, 16, 6, 7,
16, 15, 13, 17, 18, 6, 7, 11, 3, 14,
10, 14, 9, 9, 16, -1, 10, -1, -1, 12
};
const unsigned char
EvalParser::yystos_[] =
{
0, 4, 5, 15, 17, 18, 20, 21, 22, 13,
11, 0, 3, 16, 22, 22, 14, 10, 9, 16,
23, 6, 7, 10, 8, 16, 24, 12
11, 0, 3, 15, 16, 22, 22, 14, 14, 10,
9, 9, 16, 23, 6, 7, 6, 7, 10, 8,
16, 24, 12
};
const unsigned char
EvalParser::yyr1_[] =
{
0, 19, 20, 21, 22, 22, 22, 22, 22, 22,
23, 24, 24
22, 22, 23, 24, 24
};
const unsigned char
EvalParser::yyr2_[] =
{
0, 2, 1, 3, 1, 1, 6, 6, 8, 1,
1, 1, 1
0, 2, 1, 3, 1, 1, 6, 6, 6, 6,
8, 1, 1, 1, 1
};
......@@ -1018,8 +1056,8 @@ namespace isc { namespace eval {
const unsigned char
EvalParser::yyrline_[] =
{
0, 101, 101, 104, 111, 116, 121, 127, 133, 138,
142, 149, 154
0, 106, 106, 109, 116, 121, 126, 132, 138, 151,
164, 169, 173, 180, 185
};
// Print the state stack on the debug stream.
......@@ -1054,8 +1092,8 @@ namespace isc { namespace eval {
#line 21 "parser.yy" // lalr1.cc:1167
} } // isc::eval
#line 1058 "parser.cc" // lalr1.cc:1167
#line 161 "parser.yy" // lalr1.cc:1168
#line 1096 "parser.cc" // lalr1.cc:1167
#line 192 "parser.yy" // lalr1.cc:1168
void
isc::eval::EvalParser::error(const location_type& loc,
......
......@@ -45,12 +45,13 @@
#include <string>
#include <eval/token.h>
#include <eval/eval_context_decl.h>
#include <dhcp/option.h>
#include <boost/lexical_cast.hpp>
using namespace isc::dhcp;
using namespace isc::eval;
#line 54 "parser.h" // lalr1.cc:392
#line 55 "parser.h" // lalr1.cc:392
# include <cassert>
# include <cstdlib> // std::abort
......@@ -127,7 +128,7 @@ using namespace isc::eval;
#line 21 "parser.yy" // lalr1.cc:392
namespace isc { namespace eval {
#line 131 "parser.h" // lalr1.cc:392
#line 132 "parser.h" // lalr1.cc:392
......@@ -514,7 +515,7 @@ namespace isc { namespace eval {
/// Build a parser object.
EvalParser (EvalContext& ctx_yyarg);
EvalParser (EvalContext& ctx_yyarg, const Option::Universe& option_universe_yyarg);
virtual ~EvalParser ();
/// Parse.
......@@ -597,7 +598,7 @@ namespace isc { namespace eval {
// number is the opposite. If YYTABLE_NINF, syntax error.
static const unsigned char yytable_[];
static const unsigned char yycheck_[];
static const signed char yycheck_[];
// YYSTOS[STATE-NUM] -- The (internal number of the) accessing
// symbol of state STATE-NUM.
......@@ -717,7 +718,7 @@ namespace isc { namespace eval {
enum
{
yyeof_ = 0,
yylast_ = 21, ///< Last index in yytable_.
yylast_ = 29, ///< Last index in yytable_.
yynnts_ = 6, ///< Number of nonterminal symbols.
yyfinal_ = 11, ///< Termination state number.
yyterror_ = 1,
......@@ -728,6 +729,7 @@ namespace isc { namespace eval {
// User arguments.
EvalContext& ctx;
const Option::Universe& option_universe;
};
// Symbol number corresponding to token number t.
......@@ -1083,7 +1085,7 @@ namespace isc { namespace eval {
#line 21 "parser.yy" // lalr1.cc:392
} } // isc::eval
#line 1087 "parser.h" // lalr1.cc:392
#line 1089 "parser.h" // lalr1.cc:392
......
......@@ -25,6 +25,7 @@
#include <string>
#include <eval/token.h>
#include <eval/eval_context_decl.h>
#include <dhcp/option.h>
#include <boost/lexical_cast.hpp>
using namespace isc::dhcp;
......@@ -39,6 +40,10 @@ using namespace isc::eval;
{
# include "eval_context.h"
}
// Option universe: DHCPv4 or DHCPv6. This is required to use correct option
// definition set to map option names to codes.
%parse-param { const Option::Universe& option_universe }
%define api.token.prefix {TOKEN_}
%token
END 0 "end of file"
......@@ -130,6 +135,32 @@ string_expr : STRING
TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL));
ctx.expression.push_back(opt);
}
| OPTION "[" STRING "]" DOT TEXT
{
try {
// This may result in exception if the specified
// name is unknown.
TokenPtr opt(new TokenOption($3, option_universe,
TokenOption::TEXTUAL));
ctx.expression.push_back(opt);
} catch (const std::exception& ex) {
ctx.error(@3, ex.what());
}
}
| OPTION "[" STRING "]" DOT HEX
{
try {
// This may result in exception if the specified
// name is unknown.
TokenPtr opt(new TokenOption($3, option_universe,
TokenOption::HEXADECIMAL));
ctx.expression.push_back(opt);
} catch (const std::exception& ex) {
ctx.error(@3, ex.what());
}
}
| SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
{
TokenPtr sub(new TokenSubstring());
......
......@@ -16,6 +16,7 @@
#include <eval/token.h>
#include <eval/eval_context.h>
#include <eval/token.h>
#include <dhcp/option.h>
#include <dhcp/pkt4.h>
#include <boost/shared_ptr.hpp>
......@@ -94,7 +95,7 @@ public:
/// @brief checks if the given expression raises the expected message
/// when it is parsed.
void checkError(const string& expr, const string& msg) {
EvalContext eval;
EvalContext eval(Option::V4);
parsed_ = false;
try {
parsed_ = eval.parseString(expr);
......@@ -115,15 +116,15 @@ public:
// Test the parsing of a basic expression
TEST_F(EvalContextTest, basic) {
EvalContext tmp;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = tmp.parseString("option[123].text == 'MSFT'"));
EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'MSFT'"));
EXPECT_TRUE(parsed_);
}
// Test the parsing of a string terminal
TEST_F(EvalContextTest, string) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
EXPECT_TRUE(parsed_);
......@@ -140,7 +141,7 @@ TEST_F(EvalContextTest, string) {
// Test the parsing of a basic expression using integers
TEST_F(EvalContextTest, integer) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ =
eval.parseString("substring(option[123].text, 0, 2) == '42'"));
......@@ -149,7 +150,7 @@ TEST_F(EvalContextTest, integer) {
// Test the parsing of a hexstring terminal
TEST_F(EvalContextTest, hexstring) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("0x666f6f == 'foo'"));
EXPECT_TRUE(parsed_);
......@@ -164,7 +165,7 @@ TEST_F(EvalContextTest, hexstring) {
// Test the parsing of a hexstring terminal with an odd number of
// hexadecimal digits
TEST_F(EvalContextTest, oddHexstring) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("0X7 == 'foo'"));
EXPECT_TRUE(parsed_);
......@@ -178,7 +179,7 @@ TEST_F(EvalContextTest, oddHexstring) {
// Test the parsing of an equal expression
TEST_F(EvalContextTest, equal) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
EXPECT_TRUE(parsed_);
......@@ -196,7 +197,7 @@ TEST_F(EvalContextTest, equal) {
// Test the parsing of an option terminal
TEST_F(EvalContextTest, option) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'foo'"));
EXPECT_TRUE(parsed_);
......@@ -204,9 +205,20 @@ TEST_F(EvalContextTest, option) {
checkTokenOption(eval.expression.at(0), 123);
}
// Test parsing of an option identified by name.
TEST_F(EvalContextTest, optionWithName) {
EvalContext eval(Option::V4);
// Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++.
EXPECT_NO_THROW(parsed_ = eval.parseString("option['host-name'].text == 'foo'"));
EXPECT_TRUE(parsed_);
ASSERT_EQ(3, eval.expression.size());
checkTokenOption(eval.expression.at(0), 12);
}
// Test parsing of an option represented as hexadecimal string.
TEST_F(EvalContextTest, optionHex) {
EvalContext eval;
EvalContext eval(Option::