Browse Source

[5132] expressions evaluated to string added

Tomek Mrugalski 8 years ago
parent
commit
279fe78b18

+ 2 - 2
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();

+ 14 - 3
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.

+ 21 - 1
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;

+ 17 - 2
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
 

+ 44 - 1
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", "");
+}
+
 };