summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorTomek Mrugalski <tomasz@isc.org>2017-03-23 15:42:01 +0100
committerTomek Mrugalski <tomasz@isc.org>2017-03-23 15:42:01 +0100
commit279fe78b1824ecda0efcd852c45c2a37b964337e (patch)
tree97358a64f70a48ff5536b7833b1d49cc3207e1d1 /src/lib
parent[5132] evaluate renamed to evaluateBool (diff)
downloadkea-279fe78b1824ecda0efcd852c45c2a37b964337e.tar.xz
kea-279fe78b1824ecda0efcd852c45c2a37b964337e.zip
[5132] expressions evaluated to string added
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/eval/eval_context.cc4
-rw-r--r--src/lib/eval/eval_context.h17
-rw-r--r--src/lib/eval/lexer.ll22
-rw-r--r--src/lib/eval/parser.yy19
-rw-r--r--src/lib/eval/tests/evaluate_unittest.cc45
5 files changed, 98 insertions, 9 deletions
diff --git a/src/lib/eval/eval_context.cc b/src/lib/eval/eval_context.cc
index d90d71bee5..cd81796b4d 100644
--- a/src/lib/eval/eval_context.cc
+++ b/src/lib/eval/eval_context.cc
@@ -27,11 +27,11 @@ EvalContext::~EvalContext()
}
bool
-EvalContext::parseString(const std::string& str)
+EvalContext::parseString(const std::string& str, ParserType type)
{
file_ = "<string>";
string_ = str;
- scanStringBegin();
+ scanStringBegin(type);
isc::eval::EvalParser parser(*this);
parser.set_debug_level(trace_parsing_);
int res = parser.parse();
diff --git a/src/lib/eval/eval_context.h b/src/lib/eval/eval_context.h
index 053e8cf64b..eb914d2d9e 100644
--- a/src/lib/eval/eval_context.h
+++ b/src/lib/eval/eval_context.h
@@ -34,6 +34,14 @@ public:
class EvalContext
{
public:
+
+ /// @brief Specifies what type of expression the parser is expected to see
+ typedef enum {
+ PARSER_BOOL, ///< expression is expected to evaluate to bool
+ PARSER_STRING ///< expression is expected to evaluate to string
+ } ParserType;
+
+
/// @brief Default constructor.
///
/// @param option_universe Option universe: DHCPv4 or DHCPv6. This is used
@@ -48,16 +56,19 @@ public:
isc::dhcp::Expression expression;
/// @brief Method called before scanning starts on a string.
- void scanStringBegin();
+ ///
+ /// @param type specifies type of the expression to be parsed
+ void scanStringBegin(ParserType type);
/// @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
+ /// @param str string to be parsed
+ /// @param type type of the expression expected/parser type to be created
/// @return true on success.
- bool parseString(const std::string& str);
+ bool parseString(const std::string& str, ParserType type = PARSER_BOOL);
/// @brief The name of the file being parsed.
/// Used later to pass the file name to the location tracker.
diff --git a/src/lib/eval/lexer.ll b/src/lib/eval/lexer.ll
index 838123ad83..bfa4e65db0 100644
--- a/src/lib/eval/lexer.ll
+++ b/src/lib/eval/lexer.ll
@@ -25,6 +25,11 @@
// variable will be useful for logging errors.
static isc::eval::location loc;
+namespace {
+ bool start_token_flag = false;
+ isc::eval::EvalContext::ParserType start_token_value;
+};
+
// To avoid the call to exit... oops!
#define YY_FATAL_ERROR(msg) isc::eval::EvalContext::fatal(msg)
%}
@@ -77,6 +82,18 @@ addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]*
%{
// Code run each time evallex is called.
loc.step();
+
+ if (start_token_flag) {
+ start_token_flag = false;
+ switch (start_token_value) {
+ case EvalContext::PARSER_BOOL:
+ return isc::eval::EvalParser::make_TOPLEVEL_BOOL(loc);
+ default:
+ case EvalContext::PARSER_STRING:
+ return isc::eval::EvalParser::make_TOPLEVEL_STRING(loc);
+ }
+ }
+
%}
{blank}+ {
@@ -194,8 +211,11 @@ addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]*
using namespace isc::eval;
void
-EvalContext::scanStringBegin()
+EvalContext::scanStringBegin(ParserType type)
{
+ start_token_flag = true;
+ start_token_value = type;
+
loc.initialize(&file_);
eval_flex_debug = trace_scanning_;
YY_BUFFER_STATE buffer;
diff --git a/src/lib/eval/parser.yy b/src/lib/eval/parser.yy
index 53c76897b7..9d31b01a67 100644
--- a/src/lib/eval/parser.yy
+++ b/src/lib/eval/parser.yy
@@ -80,6 +80,9 @@ using namespace isc::eval;
ANY "*"
DATA "data"
ENTERPRISE "enterprise"
+
+ TOPLEVEL_BOOL "top-level bool"
+ TOPLEVEL_STRING "top-level string"
;
%token <std::string> STRING "constant string"
@@ -106,8 +109,20 @@ using namespace isc::eval;
%%
-// The whole grammar starts with an expression.
-%start expression;
+// The whole grammar starts with a 'start' symbol...
+%start start;
+
+// ... that expects either TOPLEVEL_BOOL or TOPLEVEL_STRING. Depending on which
+// token appears first, it will determine what is allowed and what it not.
+start: TOPLEVEL_BOOL expression
+ | TOPLEVEL_STRING string_expression
+;
+
+// string expression can be either a string (proper) or boolean (that is internally
+// stored as "true" or "false")
+string_expression: bool_expr
+ | string_expr
+;
// Expression can either be a single token or a (something == something) expression
diff --git a/src/lib/eval/tests/evaluate_unittest.cc b/src/lib/eval/tests/evaluate_unittest.cc
index 8106bc778d..b8a69d8180 100644
--- a/src/lib/eval/tests/evaluate_unittest.cc
+++ b/src/lib/eval/tests/evaluate_unittest.cc
@@ -294,7 +294,7 @@ TEST_F(EvaluateTest, complex) {
class ExpressionsTest : public EvaluateTest {
public:
- /// @brief Checks if expression can be parsed and evaluated
+ /// @brief Checks if expression can be parsed and evaluated to bool
///
/// There are skeleton packets created in pkt4_ and pkt6_. Make sure you
/// tweak them as needed before calling this method.
@@ -327,6 +327,39 @@ public:
EXPECT_EQ(exp_result, result) << " for expression " << expr;
}
+ /// @brief Checks if expression can be parsed and evaluated to string
+ ///
+ /// There are skeleton packets created in pkt4_ and pkt6_. Make sure you
+ /// tweak them as needed before calling this method.
+ ///
+ /// @param u universe (V4 or V6)
+ /// @param expr expression to be parsed
+ /// @param exp_result expected result (string)
+ void testExpressionString(const Option::Universe& u, const std::string& expr,
+ const std::string& exp_result) {
+
+ EvalContext eval(u);
+ string result;
+ bool parsed = false;
+
+ EXPECT_NO_THROW(parsed = eval.parseString(expr, EvalContext::PARSER_STRING))
+ << " while parsing expression " << expr;
+ EXPECT_TRUE(parsed) << " for expression " << expr;
+
+ switch (u) {
+ case Option::V4:
+ ASSERT_NO_THROW(result = evaluateString(eval.expression, *pkt4_))
+ << " for expression " << expr;
+ break;
+ case Option::V6:
+ ASSERT_NO_THROW(result = evaluateString(eval.expression, *pkt6_))
+ << " for expression " << expr;
+ break;
+ }
+
+ EXPECT_EQ(exp_result, result) << " for expression " << expr;
+ }
+
/// @brief Checks that specified expression throws expected exception.
///
/// @tparam ex exception type expected to be thrown
@@ -440,4 +473,14 @@ TEST_F(ExpressionsTest, invalidIntegers) {
// Oops, one too much.
testExpressionNegative<EvalParseError>("4294967296 == 0");
}
+
+// Tests whether expressions can be evaluated to a string.
+TEST_F(ExpressionsTest, evaluateString) {
+
+ testExpressionString(Option::V4, "option[100].hex", "hundred4");
+ testExpressionString(Option::V6, "option[100].hex", "hundred6");
+ testExpressionString(Option::V4, "option[200].hex", "");
+ testExpressionString(Option::V6, "option[200].hex", "");
+}
+
};