Browse Source

[4088] Merged trac4088fd private branch

Francis Dupont 9 years ago
parent
commit
ac00ace4dd

+ 1 - 1
src/lib/eval/Makefile.am

@@ -17,7 +17,7 @@ libkea_eval_la_SOURCES += token.cc token.h
 
 
 libkea_eval_la_SOURCES += parser.cc parser.h
 libkea_eval_la_SOURCES += parser.cc parser.h
 libkea_eval_la_SOURCES += lexer.cc
 libkea_eval_la_SOURCES += lexer.cc
-libkea_eval_la_SOURCES += eval_context.cc
+libkea_eval_la_SOURCES += eval_context.cc eval_context.h eval_context_decl.h
 
 
 nodist_libkea_eval_la_SOURCES = eval_messages.h eval_messages.cc
 nodist_libkea_eval_la_SOURCES = eval_messages.h eval_messages.cc
 
 

+ 24 - 17
src/lib/eval/eval.dox

@@ -22,9 +22,8 @@
   classification, but in the future may be also used for other applications.
   classification, but in the future may be also used for other applications.
 
 
   The external interface to the library is the @ref isc::eval::EvalContext
   The external interface to the library is the @ref isc::eval::EvalContext
-  class.  Once instantiated, it offers two major methods:
-  @ref isc::eval::EvalContext::parseFile which parses the content of a file
-  and @ref isc::eval::EvalContext::parseString, which parses the specified
+  class.  Once instantiated, it offers a major method:
+  @ref isc::eval::EvalContext::parseString, which parses the specified
   string.  Once the expression is parsed, it is converted to a collection of
   string.  Once the expression is parsed, it is converted to a collection of
   tokens that are stored in Reverse Polish Notation in
   tokens that are stored in Reverse Polish Notation in
   EvalContext::expression.
   EvalContext::expression.
@@ -66,27 +65,35 @@
 @code
 @code
 01. %start expression;
 01. %start expression;
 02.
 02.
-03. expression:
-04. token EQUAL token
-05. | token;
+03. expression : token EQUAL token
+04.            | token
+05.            ;
 06.
 06.
-07. token:
-08. STRING {
-09.    TokenPtr str(new TokenString($1));
-10.    ctx.expression.push_back(str);
-11.}
-12.| OPTION {
-13.    TokenPtr opt(new TokenOption($1));
-14.    ctx.expression.push_back(opt);
-15.};
+07. token : STRING
+08.             {
+09.                 TokenPtr str(new TokenString($1));
+10.                 ctx.expression.push_back(str);
+11.             }
+12.       | HEXSTRING
+13.             {
+14.                 TokenPtr hex(new TokenHexString($1));
+15.                 ctx.expression.push_back(hex);
+16.             }
+17.       | OPTION '[' INTEGER ']'
+18.             {
+19.                 TokenPtr opt(new TokenOption($3));
+20.                 ctx.expression.push_back(opt);
+21.              }
+22.       ;
 @endcode
 @endcode
 
 
 This code determines that the grammar starts from expression (line 1).
 This code determines that the grammar starts from expression (line 1).
 The actual definition of expression (lines 3-5) may either be a
 The actual definition of expression (lines 3-5) may either be a
 single token or an expression "token == token" (EQUAL has been defined as
 single token or an expression "token == token" (EQUAL has been defined as
 "==" elsewhere). Token is further
 "==" elsewhere). Token is further
-defined in lines 7-15: it may either be a string (lines 8-11) or option
-(lines 12-15). When the actual case is determined, the respective C++ action
+defined in lines 7-22: it may either be a string (lines 7-11),
+a hex string (lines 12-16) or option (lines 17-21).
+When the actual case is determined, the respective C++ action
 is executed. For example, if the token is a string, the TokenString class is
 is executed. For example, if the token is a string, the TokenString class is
 instantiated with the appropriate value and put onto the expression vector.
 instantiated with the appropriate value and put onto the expression vector.
 
 

+ 11 - 30
src/lib/eval/eval_context.cc

@@ -26,46 +26,27 @@ EvalContext::~EvalContext()
 {
 {
 }
 }
 
 
-int
-EvalContext::parseFile(const std::string &filename)
+bool
+EvalContext::parseString(const std::string& str)
 {
 {
-    file = filename;
-    scanBegin();
+    file_ = "<string>";
+    string_ = str;
+    scanStringBegin();
     isc::eval::EvalParser parser(*this);
     isc::eval::EvalParser parser(*this);
     parser.set_debug_level(trace_parsing_);
     parser.set_debug_level(trace_parsing_);
     int res = parser.parse();
     int res = parser.parse();
-    scanEnd();
-    return res;
-}
-
-int
-EvalContext::parseString(const std::string& str)
-{
-    /// @todo: Is there any way for the parser to read from a stream,
-    /// rather than a file? It would be better to use stringstream,
-    /// but it seems that the lexer operates on FILE* interface.
-
-    // Put the content into a file and then open that file for reading.
-    remove("/tmp/eval");
-    std::fstream f("/tmp/eval", std::ios::out);
-
-    if (!f.good()) {
-        isc_throw(isc::Unexpected, "Can't write /tmp/eval file");
-    }
-    f << str;
-    f.close();
-
-    return (parseFile("/tmp/eval"));
+    scanStringEnd();
+    return (res == 0);
 }
 }
 
 
 void
 void
-EvalContext::error(const isc::eval::location& l, const std::string& m)
+EvalContext::error(const isc::eval::location& loc, const std::string& what)
 {
 {
-    isc_throw(EvalError, l << ": " << m);
+    isc_throw(EvalParseError, loc << ": " << what);
 }
 }
 
 
 void
 void
-EvalContext::error (const std::string& m)
+EvalContext::error (const std::string& what)
 {
 {
-    isc_throw(EvalError, m);
+    isc_throw(EvalParseError, what);
 }
 }

+ 23 - 17
src/lib/eval/eval_context.h

@@ -17,6 +17,7 @@
 #include <string>
 #include <string>
 #include <map>
 #include <map>
 #include <eval/parser.h>
 #include <eval/parser.h>
+#include <eval/eval_context_decl.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
 // Tell Flex the lexer's prototype ...
 // Tell Flex the lexer's prototype ...
@@ -25,10 +26,13 @@
 // ... and declare it for the parser's sake.
 // ... and declare it for the parser's sake.
 YY_DECL;
 YY_DECL;
 
 
+namespace isc {
+namespace eval {
+
 /// @brief Evaluation error exception raised when trying to parse an axceptions.
 /// @brief Evaluation error exception raised when trying to parse an axceptions.
-class EvalError : public isc::Exception {
+class EvalParseError : public isc::Exception {
 public:
 public:
-    EvalError(const char* file, size_t line, const char* what) :
+    EvalParseError(const char* file, size_t line, const char* what) :
         isc::Exception(file, line, what) { };
         isc::Exception(file, line, what) { };
 };
 };
 
 
@@ -46,32 +50,30 @@ public:
     /// @brief Parsed expression (output tokens are stored here)
     /// @brief Parsed expression (output tokens are stored here)
     isc::dhcp::Expression expression;
     isc::dhcp::Expression expression;
 
 
-    /// @brief Method called before scanning starts.
-    void scanBegin();
+    /// @brief Method called before scanning starts on a string.
+    void scanStringBegin();
 
 
-    /// @brief Method called after the last tokens are scanned.
-    void scanEnd();
+    /// @brief Method called after the last tokens are scanned from a string.
+    void scanStringEnd();
     
     
-    /// @brief Runs the parser on specified file.
-    ///
-    /// @param filename
-    /// Return 0 on success.
-    int parseFile(const std::string& filename);
-
     /// @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
-    int parseString(const std::string& str);
+    /// @return true on success.
+    bool parseString(const std::string& str);
 
 
     /// @brief The name of the file being parsed.
     /// @brief The name of the file being parsed.
     /// Used later to pass the file name to the location tracker.
     /// Used later to pass the file name to the location tracker.
-    std::string file;
+    std::string file_;
+
+    /// @brief The string being parsed.
+    std::string string_;
 
 
     /// @brief Error handler
     /// @brief Error handler
     ///
     ///
-    /// @param l location within the parsed file when experienced a problem.
+    /// @param loc location within the parsed file when experienced a problem.
     /// @param what string explaining the nature of the error.
     /// @param what string explaining the nature of the error.
-    void error(const isc::eval::location& l, const std::string& what);
+    void error(const isc::eval::location& loc, const std::string& what);
 
 
     /// @brief Error handler
     /// @brief Error handler
     ///
     ///
@@ -87,4 +89,8 @@ public:
     bool trace_parsing_;
     bool trace_parsing_;
   
   
 };
 };
-#endif // ! EVALCONTEXT_H
+
+}; // end of isc::eval namespace
+}; // end of isc namespace
+
+#endif

+ 28 - 0
src/lib/eval/eval_context_decl.h

@@ -0,0 +1,28 @@
+// 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 EVAL_CONTEXT_DECL_H
+#define EVAL_CONTEXT_DECL_H
+
+/// @file eval_context_decl.h Forward declaration of the EvalContext class
+
+namespace isc {
+namespace eval {
+
+class EvalContext;
+
+}; // end of isc::eval namespace
+}; // end of isc namespace
+
+#endif

+ 0 - 5
src/lib/eval/eval_messages.mes

@@ -18,8 +18,3 @@ $NAMESPACE isc::dhcp
 This debug message indicates that the expression has been evaluated
 This debug message indicates that the expression has been evaluated
 to said value. This message is mostly useful during debugging of the
 to said value. This message is mostly useful during debugging of the
 client classification expressions.
 client classification expressions.
-
-% EVAL_SUBSTRING_BAD_PARAM_CONVERSION starting %1, length %2
-This debug message indicates that the parameter for the starting postion
-or length of the substring couldn't be converted to an integer.  In this
-case the substring routine returns an empty string.

+ 153 - 142
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 37
+#define YY_FLEX_SUBMINOR_VERSION 39
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #define FLEX_BETA
 #define FLEX_BETA
 #endif
 #endif
@@ -230,6 +230,13 @@ extern FILE *yyin, *yyout;
                     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) \
@@ -438,6 +445,8 @@ 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 );
@@ -460,8 +469,8 @@ static void yy_fatal_error (yyconst char msg[]  );
 	(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 11
-#define YY_END_OF_BUFFER 12
+#define YY_NUM_RULES 16
+#define YY_END_OF_BUFFER 17
 /* 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
@@ -469,12 +478,13 @@ 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[36] =
+static yyconst flex_int16_t yy_accept[44] =
     {   0,
     {   0,
-        0,    0,   12,   10,    1,    2,   10,    7,    8,    9,
-       10,   10,   10,    1,    2,    3,    0,    5,    0,    0,
-        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-        0,    0,    4,    6,    0
+        0,    0,   17,   15,    1,    2,   15,   10,   11,   14,
+       15,    5,    5,   15,   12,   13,   15,   15,   15,    1,
+        2,    0,    3,    5,    0,    6,    0,    0,    0,    4,
+        9,    0,    0,    0,    0,    0,    0,    7,    0,    0,
+        0,    8,    0
     } ;
     } ;
 
 
 static yyconst flex_int32_t yy_ec[256] =
 static yyconst flex_int32_t yy_ec[256] =
@@ -483,16 +493,16 @@ static yyconst flex_int32_t yy_ec[256] =
         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,    2,    1,    1,    1,    1,    1,    1,    4,    5,
         1,    2,    1,    1,    1,    1,    1,    1,    4,    5,
-        6,    1,    1,    7,    1,    1,    1,    8,    8,    8,
-        8,    8,    8,    8,    8,    8,    8,    1,    1,    1,
-        9,    1,    1,    1,   10,   10,   10,   10,   10,   10,
-       10,   10,   10,   10,   10,   10,   10,   10,   10,   10,
-       10,   10,   10,   10,   10,   10,   10,   10,   10,   10,
-       11,    1,   12,    1,   10,    1,   10,   13,   10,   10,
-
-       10,   10,   14,   10,   15,   10,   10,   10,   10,   16,
-       17,   18,   10,   19,   20,   21,   22,   10,   10,   10,
-       10,   10,    1,    1,    1,    1,    1,    1,    1,    1,
+        6,    1,    1,    7,    8,    1,    1,    9,   10,   10,
+       10,   10,   10,   10,   10,   10,   10,    1,    1,    1,
+       11,    1,    1,    1,   12,   12,   12,   12,   12,   12,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,   13,    1,    1,
+       14,    1,   15,    1,    1,    1,   16,   17,   12,   12,
+
+       12,   12,   18,    1,   19,    1,    1,   20,    1,   21,
+       22,   23,    1,   24,   25,   26,   27,    1,    1,   13,
+        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,    1,    1,
@@ -509,57 +519,61 @@ 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[23] =
+static yyconst flex_int32_t yy_meta[28] =
     {   0,
     {   0,
-        1,    1,    1,    2,    1,    1,    1,    2,    1,    2,
-        1,    1,    2,    2,    2,    2,    2,    2,    2,    2,
-        2,    2
+        1,    1,    2,    1,    1,    1,    1,    1,    3,    3,
+        1,    3,    1,    1,    1,    3,    3,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_base[37] =
+static yyconst flex_int16_t yy_base[46] =
     {   0,
     {   0,
-        0,    0,   48,   49,   45,   43,   41,   49,   49,   49,
-       35,   25,   20,   39,   37,   49,   35,   49,   17,   24,
-       21,   15,   17,   12,   16,   12,   19,   14,   20,   10,
-       15,   11,   49,   49,   49,   22
+        0,    0,   62,   63,   59,   57,   55,   63,   63,   63,
+       19,   21,   23,   47,   63,   63,   37,   33,   28,   52,
+       50,   48,   63,   26,    0,   63,   31,   24,   32,    0,
+       63,   29,   22,   24,   19,   23,   19,   63,   23,   20,
+       22,   63,   63,   36,   35
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_def[37] =
+static yyconst flex_int16_t yy_def[46] =
     {   0,
     {   0,
-       35,    1,   35,   35,   35,   35,   36,   35,   35,   35,
-       35,   35,   35,   35,   35,   35,   36,   35,   35,   35,
-       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
-       35,   35,   35,   35,    0,   35
+       43,    1,   43,   43,   43,   43,   44,   43,   43,   43,
+       43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
+       43,   44,   43,   43,   45,   43,   43,   43,   43,   45,
+       43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
+       43,   43,    0,   43,   43
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_nxt[72] =
+static yyconst flex_int16_t yy_nxt[91] =
     {   0,
     {   0,
-        4,    5,    6,    7,    8,    9,   10,    4,   11,    4,
-        4,    4,    4,    4,    4,    4,   12,    4,    4,   13,
-        4,    4,   31,   17,   34,   32,   33,   31,   30,   29,
-       28,   27,   26,   25,   24,   23,   22,   21,   16,   15,
-       14,   20,   19,   18,   16,   15,   14,   35,    3,   35,
-       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
-       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
-       35
+        4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
+       14,    4,    4,   15,   16,   17,    4,    4,    4,    4,
+        4,   18,    4,    4,   19,    4,    4,   24,   24,   24,
+       24,   24,   24,   25,   24,   24,   22,   30,   22,   42,
+       41,   40,   39,   38,   37,   36,   35,   34,   33,   32,
+       31,   23,   21,   20,   29,   28,   27,   26,   23,   21,
+       20,   43,    3,   43,   43,   43,   43,   43,   43,   43,
+       43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
+       43,   43,   43,   43,   43,   43,   43,   43,   43,   43
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_chk[72] =
+static yyconst flex_int16_t yy_chk[91] =
     {   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,   31,   36,   32,   30,   31,   29,   28,   27,
-       26,   25,   24,   23,   22,   21,   20,   19,   17,   15,
-       14,   13,   12,   11,    7,    6,    5,    3,   35,   35,
-       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
-       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
-       35
+        1,    1,    1,    1,    1,    1,    1,   11,   11,   12,
+       12,   13,   13,   12,   24,   24,   44,   45,   44,   41,
+       40,   39,   37,   36,   35,   34,   33,   32,   29,   28,
+       27,   22,   21,   20,   19,   18,   17,   14,    7,    6,
+        5,    3,   43,   43,   43,   43,   43,   43,   43,   43,
+       43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
+       43,   43,   43,   43,   43,   43,   43,   43,   43,   43
     } ;
     } ;
 
 
 /* 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[12] =
+static yyconst flex_int32_t yy_rule_can_match_eol[17] =
     {   0,
     {   0,
-0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,     };
+0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,     };
 
 
 static yy_state_type yy_last_accepting_state;
 static yy_state_type yy_last_accepting_state;
 static char *yy_last_accepting_cpos;
 static char *yy_last_accepting_cpos;
@@ -567,9 +581,10 @@ static char *yy_last_accepting_cpos;
 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[11] =
+static yyconst flex_int16_t yy_rule_linenum[16] =
     {   0,
     {   0,
-       83,   87,   93,  103,  154,  155,  156,  157,  158,  160
+       83,   87,   93,  103,  109,  123,  124,  125,  126,  127,
+      128,  129,  130,  131,  133
     } ;
     } ;
 
 
 /* The intent behind this definition is that it'll catch
 /* The intent behind this definition is that it'll catch
@@ -636,7 +651,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 640 "lexer.cc"
+#line 655 "lexer.cc"
 
 
 #define INITIAL 0
 #define INITIAL 0
 
 
@@ -875,17 +890,6 @@ 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 76 "lexer.ll"
-
-
-
-    // Code run each time yylex is called.
-    loc.step();
-
-
-#line 888 "lexer.cc"
-
 	if ( !(yy_init) )
 	if ( !(yy_init) )
 		{
 		{
 		(yy_init) = 1;
 		(yy_init) = 1;
@@ -920,6 +924,18 @@ YY_DECL
 		yy_load_buffer_state( );
 		yy_load_buffer_state( );
 		}
 		}
 
 
+	{
+/* %% [7.0] user's declarations go here */
+#line 76 "lexer.ll"
+
+
+
+    // Code run each time yylex is called.
+    loc.step();
+
+
+#line 938 "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 */
@@ -938,7 +954,7 @@ YY_DECL
 yy_match:
 yy_match:
 		do
 		do
 			{
 			{
-			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
 			if ( yy_accept[yy_current_state] )
 			if ( yy_accept[yy_current_state] )
 				{
 				{
 				(yy_last_accepting_state) = yy_current_state;
 				(yy_last_accepting_state) = yy_current_state;
@@ -947,13 +963,13 @@ yy_match:
 			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 >= 36 )
+				if ( yy_current_state >= 44 )
 					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_cp;
 			++yy_cp;
 			}
 			}
-		while ( yy_current_state != 35 );
+		while ( yy_current_state != 43 );
 		yy_cp = (yy_last_accepting_cpos);
 		yy_cp = (yy_last_accepting_cpos);
 		yy_current_state = (yy_last_accepting_state);
 		yy_current_state = (yy_last_accepting_state);
 
 
@@ -967,7 +983,7 @@ yy_find_action:
 
 
 		if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
 		if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
 			{
 			{
-			int yyl;
+			yy_size_t yyl;
 			for ( yyl = 0; yyl < yyleng; ++yyl )
 			for ( yyl = 0; yyl < yyleng; ++yyl )
 				if ( yytext[yyl] == '\n' )
 				if ( yytext[yyl] == '\n' )
 					   
 					   
@@ -982,13 +998,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 < 11 )
+			else if ( yy_act < 16 )
 				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 == 11 )
+			else if ( yy_act == 16 )
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				         yytext );
 				         yytext );
-			else if ( yy_act == 12 )
+			else if ( yy_act == 17 )
 				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 );
@@ -1039,96 +1055,88 @@ case 4:
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 103 "lexer.ll"
 #line 103 "lexer.ll"
 {
 {
-    // option[123] token found. Let's see if the numeric value can be
-    // converted to integer and if it has a reasonable value.
-    // yytext contains the whole expression (.e.g. option[123]). We need
-    // to trim it down to just the code, which will be transformed to
-    // integer.
+    // A hex string has been matched. It contains the '0x' or '0X' header
+    // followed by at least one hexadecimal digit.
+    return isc::eval::EvalParser::make_HEXSTRING(yytext, loc);
+}
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 109 "lexer.ll"
+{
+    // An integer was found.
     std::string tmp(yytext);
     std::string tmp(yytext);
 
 
-    // Sanity check if the token is at least 9 (strlen("option[X]")) long.
-    // This should never happen as it would indicate bison bug.
-    if (tmp.length() < 9) {
-        driver.error(loc, "The string matched (" + tmp + ") is too short,"
-                     " expected at least 9 (option[X]) characters");
-    }
-    size_t pos = tmp.find("[");
-    if (pos == std::string::npos) {
-        driver.error(loc, "The string matched (" + tmp + ") is invalid,"
-                     " as it does not contain opening bracket.");
-    }
-    // Let's get rid of all the text before [, including [.
-    tmp = tmp.substr(pos + 1);
-
-    // And finally remove the trailing ].
-    pos = tmp.find("]");
-    if (pos == std::string::npos) {
-        driver.error(loc, "The string matched (" + tmp + ") is invalid,"
-                     " as it does not contain closing bracket.");
-    }
-    tmp = tmp.substr(0, pos);
-
-    uint16_t n = 0;
     try {
     try {
-        n = boost::lexical_cast<int>(tmp);
+        static_cast<void>(boost::lexical_cast<int>(tmp));
     } catch (const boost::bad_lexical_cast &) {
     } catch (const boost::bad_lexical_cast &) {
-        driver.error(loc, "Failed to convert specified option code to "
-                     "number ('" + tmp + "' in expression " + std::string(yytext));
-    }
-
-    // 65535 is the maximum value of the option code in DHCPv6. We want the
-    // code to be the same for v4 and v6, so let's ignore for a moment that
-    // max. option code in DHCPv4 is 255.
-    /// @todo: Maybe add a flag somewhere in EvalContext to indicate if we're
-    /// running in v4 (allowed max 255) or v6 (allowed max 65535).
-    if (n<0 || n>65535) {
-        driver.error(loc, "Option code has invalid value in " +
-                     std::string(yytext) + ". Allowed range: 0..65535");
+        driver.error(loc, "Failed to convert " + tmp + " to an integer.");
     }
     }
 
 
-    return isc::eval::EvalParser::make_OPTION(n, loc);
+    // The parser needs the string form as double conversion is no lossless
+    return isc::eval::EvalParser::make_INTEGER(tmp, loc);
 }
 }
 	YY_BREAK
 	YY_BREAK
-case 5:
+case 6:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 154 "lexer.ll"
+#line 123 "lexer.ll"
 return isc::eval::EvalParser::make_EQUAL(loc);
 return isc::eval::EvalParser::make_EQUAL(loc);
 	YY_BREAK
 	YY_BREAK
-case 6:
+case 7:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 155 "lexer.ll"
+#line 124 "lexer.ll"
+return isc::eval::EvalParser::make_OPTION(loc);
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 125 "lexer.ll"
 return isc::eval::EvalParser::make_SUBSTRING(loc);
 return isc::eval::EvalParser::make_SUBSTRING(loc);
 	YY_BREAK
 	YY_BREAK
-case 7:
+case 9:
+YY_RULE_SETUP
+#line 126 "lexer.ll"
+return isc::eval::EvalParser::make_ALL(loc);
+	YY_BREAK
+case 10:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 156 "lexer.ll"
+#line 127 "lexer.ll"
 return isc::eval::EvalParser::make_LPAREN(loc);
 return isc::eval::EvalParser::make_LPAREN(loc);
 	YY_BREAK
 	YY_BREAK
-case 8:
+case 11:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 157 "lexer.ll"
+#line 128 "lexer.ll"
 return isc::eval::EvalParser::make_RPAREN(loc);
 return isc::eval::EvalParser::make_RPAREN(loc);
 	YY_BREAK
 	YY_BREAK
-case 9:
+case 12:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 158 "lexer.ll"
+#line 129 "lexer.ll"
+return isc::eval::EvalParser::make_LBRACKET(loc);
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 130 "lexer.ll"
+return isc::eval::EvalParser::make_RBRACKET(loc);
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 131 "lexer.ll"
 return isc::eval::EvalParser::make_COMA(loc);
 return isc::eval::EvalParser::make_COMA(loc);
 	YY_BREAK
 	YY_BREAK
-case 10:
+case 15:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 160 "lexer.ll"
+#line 133 "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 161 "lexer.ll"
+#line 134 "lexer.ll"
 return isc::eval::EvalParser::make_END(loc);
 return isc::eval::EvalParser::make_END(loc);
 	YY_BREAK
 	YY_BREAK
-case 11:
+case 16:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 162 "lexer.ll"
+#line 135 "lexer.ll"
 ECHO;
 ECHO;
 	YY_BREAK
 	YY_BREAK
-#line 1132 "lexer.cc"
+#line 1140 "lexer.cc"
 
 
 	case YY_END_OF_BUFFER:
 	case YY_END_OF_BUFFER:
 		{
 		{
@@ -1259,6 +1267,7 @@ 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 */
 
 
@@ -1440,7 +1449,7 @@ static int yy_get_next_buffer (void)
 		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 >= 36 )
+			if ( yy_current_state >= 44 )
 				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];
@@ -1473,11 +1482,11 @@ static int yy_get_next_buffer (void)
 	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 >= 36 )
+		if ( yy_current_state >= 44 )
 			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 == 35);
+	yy_is_jam = (yy_current_state == 43);
 
 
 		return yy_is_jam ? 0 : yy_current_state;
 		return yy_is_jam ? 0 : yy_current_state;
 }
 }
@@ -1952,7 +1961,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 n;
-	int i;
+	yy_size_t 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;
@@ -2215,26 +2224,28 @@ void yyfree (void * ptr )
 
 
 /* %ok-for-header */
 /* %ok-for-header */
 
 
-#line 162 "lexer.ll"
+#line 135 "lexer.ll"
+
 
 
 
 
+using namespace isc::eval;
 
 
 void
 void
-EvalContext::scanBegin()
+EvalContext::scanStringBegin()
 {
 {
+    loc.initialize(&file_);
     yy_flex_debug = trace_scanning_;
     yy_flex_debug = trace_scanning_;
-    if (file.empty () || file == "-") {
-        yyin = stdin;
-    }
-    else if (!(yyin = fopen(file.c_str (), "r"))) {
-        error("cannot open " + file + ": " + strerror(errno));
+    YY_BUFFER_STATE buffer;
+    buffer = yy_scan_bytes(string_.c_str(),string_.size());
+    if (!buffer) {
+        error("cannot scan string");
         exit(EXIT_FAILURE);
         exit(EXIT_FAILURE);
     }
     }
 }
 }
 
 
 void
 void
-EvalContext::scanEnd()
+EvalContext::scanStringEnd()
 {
 {
-    fclose(yyin);
+    yy_delete_buffer(YY_CURRENT_BUFFER);
 }
 }
 
 

+ 29 - 54
src/lib/eval/lexer.ll

@@ -62,9 +62,9 @@ static isc::eval::location loc;
 
 
 /* These are not token expressions yet, just convenience expressions that
 /* These are not token expressions yet, just convenience expressions that
    can be used during actual token definitions. */
    can be used during actual token definitions. */
-int   [0-9]+
+int   \-?[0-9]+
+hex   [0-9a-fA-F]+
 blank [ \t]
 blank [ \t]
-str [a-zA-Z_0-9]*
 
 
 %{
 %{
 // This code run each time a pattern is matched. It updates the location
 // This code run each time a pattern is matched. It updates the location
@@ -90,7 +90,7 @@ str [a-zA-Z_0-9]*
     loc.step();
     loc.step();
 }
 }
 
 
-\'{str}\' {
+\'[^\'\n]*\' {
     // A string has been matched. It contains the actual string and single quotes.
     // A string has been matched. It contains the actual string and single quotes.
     // We need to get those quotes out of the way and just use its content, e.g.
     // We need to get those quotes out of the way and just use its content, e.g.
     // for 'foo' we should get foo
     // for 'foo' we should get foo
@@ -100,82 +100,57 @@ str [a-zA-Z_0-9]*
     return isc::eval::EvalParser::make_STRING(tmp, loc);
     return isc::eval::EvalParser::make_STRING(tmp, loc);
 }
 }
 
 
-option\[{int}\] {
-    // option[123] token found. Let's see if the numeric value can be
-    // converted to integer and if it has a reasonable value.
-    // yytext contains the whole expression (.e.g. option[123]). We need
-    // to trim it down to just the code, which will be transformed to
-    // integer.
-    std::string tmp(yytext);
+0[xX]{hex} {
+    // A hex string has been matched. It contains the '0x' or '0X' header
+    // followed by at least one hexadecimal digit.
+    return isc::eval::EvalParser::make_HEXSTRING(yytext, loc);
+}
 
 
-    // Sanity check if the token is at least 9 (strlen("option[X]")) long.
-    // This should never happen as it would indicate bison bug.
-    if (tmp.length() < 9) {
-        driver.error(loc, "The string matched (" + tmp + ") is too short,"
-                     " expected at least 9 (option[X]) characters");
-    }
-    size_t pos = tmp.find("[");
-    if (pos == std::string::npos) {
-        driver.error(loc, "The string matched (" + tmp + ") is invalid,"
-                     " as it does not contain opening bracket.");
-    }
-    // Let's get rid of all the text before [, including [.
-    tmp = tmp.substr(pos + 1);
-
-    // And finally remove the trailing ].
-    pos = tmp.find("]");
-    if (pos == std::string::npos) {
-        driver.error(loc, "The string matched (" + tmp + ") is invalid,"
-                     " as it does not contain closing bracket.");
-    }
-    tmp = tmp.substr(0, pos);
+{int} {
+    // An integer was found.
+    std::string tmp(yytext);
 
 
-    uint16_t n = 0;
     try {
     try {
-        n = boost::lexical_cast<int>(tmp);
+        static_cast<void>(boost::lexical_cast<int>(tmp));
     } catch (const boost::bad_lexical_cast &) {
     } catch (const boost::bad_lexical_cast &) {
-        driver.error(loc, "Failed to convert specified option code to "
-                     "number ('" + tmp + "' in expression " + std::string(yytext));
-    }
-
-    // 65535 is the maximum value of the option code in DHCPv6. We want the
-    // code to be the same for v4 and v6, so let's ignore for a moment that
-    // max. option code in DHCPv4 is 255.
-    /// @todo: Maybe add a flag somewhere in EvalContext to indicate if we're
-    /// running in v4 (allowed max 255) or v6 (allowed max 65535).
-    if (n<0 || n>65535) {
-        driver.error(loc, "Option code has invalid value in " +
-                     std::string(yytext) + ". Allowed range: 0..65535");
+        driver.error(loc, "Failed to convert " + tmp + " to an integer.");
     }
     }
 
 
-    return isc::eval::EvalParser::make_OPTION(n, loc);
+    // The parser needs the string form as double conversion is no lossless
+    return isc::eval::EvalParser::make_INTEGER(tmp, loc);
 }
 }
 
 
 "=="        return isc::eval::EvalParser::make_EQUAL(loc);
 "=="        return isc::eval::EvalParser::make_EQUAL(loc);
+"option"    return isc::eval::EvalParser::make_OPTION(loc);
 "substring" return isc::eval::EvalParser::make_SUBSTRING(loc);
 "substring" return isc::eval::EvalParser::make_SUBSTRING(loc);
+"all"       return isc::eval::EvalParser::make_ALL(loc);
 "("         return isc::eval::EvalParser::make_LPAREN(loc);
 "("         return isc::eval::EvalParser::make_LPAREN(loc);
 ")"         return isc::eval::EvalParser::make_RPAREN(loc);
 ")"         return isc::eval::EvalParser::make_RPAREN(loc);
+"["         return isc::eval::EvalParser::make_LBRACKET(loc);
+"]"         return isc::eval::EvalParser::make_RBRACKET(loc);
 ","         return isc::eval::EvalParser::make_COMA(loc);
 ","         return isc::eval::EvalParser::make_COMA(loc);
 
 
 .          driver.error (loc, "Invalid character: " + std::string(yytext));
 .          driver.error (loc, "Invalid character: " + std::string(yytext));
 <<EOF>>    return isc::eval::EvalParser::make_END(loc);
 <<EOF>>    return isc::eval::EvalParser::make_END(loc);
 %%
 %%
 
 
+using namespace isc::eval;
+
 void
 void
-EvalContext::scanBegin()
+EvalContext::scanStringBegin()
 {
 {
+    loc.initialize(&file_);
     yy_flex_debug = trace_scanning_;
     yy_flex_debug = trace_scanning_;
-    if (file.empty () || file == "-") {
-        yyin = stdin;
-    }
-    else if (!(yyin = fopen(file.c_str (), "r"))) {
-        error("cannot open " + file + ": " + strerror(errno));
+    YY_BUFFER_STATE buffer;
+    buffer = yy_scan_bytes(string_.c_str(), string_.size());
+    if (!buffer) {
+        error("cannot scan string");
         exit(EXIT_FAILURE);
         exit(EXIT_FAILURE);
     }
     }
 }
 }
 
 
 void
 void
-EvalContext::scanEnd()
+EvalContext::scanStringEnd()
 {
 {
-    fclose(yyin);
+    yy_delete_buffer(YY_CURRENT_BUFFER);
 }
 }

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

@@ -1,3 +1,4 @@
+// Generated 2015114
 // 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++

+ 138 - 78
src/lib/eval/parser.cc

@@ -49,7 +49,7 @@
 
 
 #line 51 "parser.cc" // lalr1.cc:412
 #line 51 "parser.cc" // lalr1.cc:412
 // Unqualified %code blocks.
 // Unqualified %code blocks.
-#line 42 "parser.yy" // lalr1.cc:413
+#line 39 "parser.yy" // lalr1.cc:413
 
 
 # include "eval_context.h"
 # include "eval_context.h"
 
 
@@ -251,11 +251,10 @@ namespace isc { namespace eval {
   {
   {
       switch (that.type_get ())
       switch (that.type_get ())
     {
     {
-      case 9: // "option code"
-        value.move< int > (that.value);
-        break;
-
-      case 8: // "constant string"
+      case 12: // "constant string"
+      case 13: // "integer"
+      case 14: // "constant hexstring"
+      case 15: // TOKEN
         value.move< std::string > (that.value);
         value.move< std::string > (that.value);
         break;
         break;
 
 
@@ -274,11 +273,10 @@ namespace isc { namespace eval {
     state = that.state;
     state = that.state;
       switch (that.type_get ())
       switch (that.type_get ())
     {
     {
-      case 9: // "option code"
-        value.copy< int > (that.value);
-        break;
-
-      case 8: // "constant string"
+      case 12: // "constant string"
+      case 13: // "integer"
+      case 14: // "constant hexstring"
+      case 15: // TOKEN
         value.copy< std::string > (that.value);
         value.copy< std::string > (that.value);
         break;
         break;
 
 
@@ -318,18 +316,32 @@ namespace isc { namespace eval {
         << yysym.location << ": ";
         << yysym.location << ": ";
     switch (yytype)
     switch (yytype)
     {
     {
-            case 8: // "constant string"
+            case 12: // "constant string"
+
+#line 61 "parser.yy" // lalr1.cc:636
+        { yyoutput << yysym.value.template as< std::string > (); }
+#line 324 "parser.cc" // lalr1.cc:636
+        break;
+
+      case 13: // "integer"
 
 
-#line 56 "parser.yy" // lalr1.cc:636
+#line 61 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 326 "parser.cc" // lalr1.cc:636
+#line 331 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 9: // "option code"
+      case 14: // "constant hexstring"
 
 
-#line 56 "parser.yy" // lalr1.cc:636
-        { yyoutput << yysym.value.template as< int > (); }
-#line 333 "parser.cc" // lalr1.cc:636
+#line 61 "parser.yy" // lalr1.cc:636
+        { yyoutput << yysym.value.template as< std::string > (); }
+#line 338 "parser.cc" // lalr1.cc:636
+        break;
+
+      case 15: // TOKEN
+
+#line 61 "parser.yy" // lalr1.cc:636
+        { yyoutput << yysym.value.template as< std::string > (); }
+#line 345 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
 
 
@@ -441,15 +453,6 @@ namespace isc { namespace eval {
     YYCDEBUG << "Starting parse" << std::endl;
     YYCDEBUG << "Starting parse" << std::endl;
 
 
 
 
-    // User initialization code.
-    #line 35 "parser.yy" // lalr1.cc:745
-{
-  // Initialize the initial location.
-  yyla.location.begin.filename = yyla.location.end.filename = &ctx.file;
-}
-
-#line 452 "parser.cc" // lalr1.cc:745
-
     /* Initialize the stack.  The initial state will be set in
     /* Initialize the stack.  The initial state will be set in
        yynewstate, since the latter expects the semantical and the
        yynewstate, since the latter expects the semantical and the
        location values to have been already stored, initialize these
        location values to have been already stored, initialize these
@@ -538,11 +541,10 @@ namespace isc { namespace eval {
          when using variants.  */
          when using variants.  */
         switch (yyr1_[yyn])
         switch (yyr1_[yyn])
     {
     {
-      case 9: // "option code"
-        yylhs.value.build< int > ();
-        break;
-
-      case 8: // "constant string"
+      case 12: // "constant string"
+      case 13: // "integer"
+      case 14: // "constant hexstring"
+      case 15: // TOKEN
         yylhs.value.build< std::string > ();
         yylhs.value.build< std::string > ();
         break;
         break;
 
 
@@ -563,43 +565,93 @@ namespace isc { namespace eval {
         {
         {
           switch (yyn)
           switch (yyn)
             {
             {
-  case 2:
-#line 64 "parser.yy" // lalr1.cc:859
+  case 3:
+#line 73 "parser.yy" // lalr1.cc:859
     {
     {
-    TokenPtr eq(new TokenEqual());
-    ctx.expression.push_back(eq);
-}
-#line 573 "parser.cc" // lalr1.cc:859
+                    TokenPtr eq(new TokenEqual());
+                    ctx.expression.push_back(eq);
+                }
+#line 575 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 4:
   case 4:
-#line 71 "parser.yy" // lalr1.cc:859
+#line 80 "parser.yy" // lalr1.cc:859
     {
     {
-    TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
-    ctx.expression.push_back(str);
-}
-#line 582 "parser.cc" // lalr1.cc:859
+                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
+                      ctx.expression.push_back(str);
+                  }
+#line 584 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 5:
   case 5:
-#line 75 "parser.yy" // lalr1.cc:859
+#line 85 "parser.yy" // lalr1.cc:859
     {
     {
-    TokenPtr opt(new TokenOption(yystack_[0].value.as< int > ()));
-    ctx.expression.push_back(opt);
-}
-#line 591 "parser.cc" // lalr1.cc:859
+                      TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
+                      ctx.expression.push_back(hex);
+                  }
+#line 593 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
   case 6:
   case 6:
-#line 79 "parser.yy" // lalr1.cc:859
+#line 90 "parser.yy" // lalr1.cc:859
     {
     {
-    /* push back TokenSubstring */
-  }
-#line 599 "parser.cc" // lalr1.cc:859
+                      int n;
+                      try {
+                          n  = boost::lexical_cast<int>(yystack_[1].value.as< std::string > ());
+                      } catch (const boost::bad_lexical_cast &) {
+                          // This can't happen...
+                          ctx.error(yystack_[1].location,
+                                    "Option code has invalid value in " + yystack_[1].value.as< std::string > ());
+                      }
+                      if (n < 0 || n > 65535) {
+                          ctx.error(yystack_[1].location,
+                                    "Option code has invalid value in "
+                                    + yystack_[1].value.as< std::string > () + ". Allowed range: 0..65535");
+                      }
+                      TokenPtr opt(new TokenOption(static_cast<uint16_t>(n)));
+                      ctx.expression.push_back(opt);
+                  }
+#line 615 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
+  case 7:
+#line 108 "parser.yy" // lalr1.cc:859
+    {
+                      TokenPtr sub(new TokenSubstring());
+                      ctx.expression.push_back(sub);
+                  }
+#line 624 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 9:
+#line 117 "parser.yy" // lalr1.cc:859
+    {
+                     TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
+                     ctx.expression.push_back(str);
+                 }
+#line 633 "parser.cc" // lalr1.cc:859
+    break;
 
 
-#line 603 "parser.cc" // lalr1.cc:859
+  case 10:
+#line 124 "parser.yy" // lalr1.cc:859
+    {
+                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
+                      ctx.expression.push_back(str);
+                  }
+#line 642 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 11:
+#line 129 "parser.yy" // lalr1.cc:859
+    {
+                     TokenPtr str(new TokenString("all"));
+                     ctx.expression.push_back(str);
+                 }
+#line 651 "parser.cc" // lalr1.cc:859
+    break;
+
+
+#line 655 "parser.cc" // lalr1.cc:859
             default:
             default:
               break;
               break;
             }
             }
@@ -854,67 +906,72 @@ namespace isc { namespace eval {
   }
   }
 
 
 
 
-  const signed char EvalParser::yypact_ninf_ = -7;
+  const signed char EvalParser::yypact_ninf_ = -9;
 
 
   const signed char EvalParser::yytable_ninf_ = -1;
   const signed char EvalParser::yytable_ninf_ = -1;
 
 
   const signed char
   const signed char
   EvalParser::yypact_[] =
   EvalParser::yypact_[] =
   {
   {
-       0,    -5,    -7,    -7,     3,     7,     0,    -7,     0,     1,
-      -7,     0,     6,     0,     5,    -7
+      -4,    -7,    -2,    -9,    -9,    -9,     7,    -9,     6,     0,
+      -4,    -9,    -4,     3,     8,    -9,    -9,     4,    -9,     9,
+      -1,    -9,    -9,    10,    -9
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yydefact_[] =
   EvalParser::yydefact_[] =
   {
   {
-       0,     0,     4,     5,     0,     3,     0,     1,     0,     0,
-       2,     0,     0,     0,     0,     6
+       0,     0,     0,     4,     5,     8,     0,     2,     0,     0,
+       0,     1,     0,     0,     0,     3,     6,     0,     9,     0,
+       0,    11,    10,     0,     7
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yypgoto_[] =
   EvalParser::yypgoto_[] =
   {
   {
-      -7,    -7,    -6
+      -9,    -9,    -9,    -8,    -9,    -9
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yydefgoto_[] =
   EvalParser::yydefgoto_[] =
   {
   {
-      -1,     4,     5
+      -1,     6,     7,     8,    19,    23
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yytable_[] =
   EvalParser::yytable_[] =
   {
   {
-       9,     6,    10,     7,     1,    12,    11,    14,     2,     3,
-       8,    13,    15
+       1,     2,    14,     9,    15,    21,    10,    11,     3,    12,
+       4,     5,    22,    13,    16,    17,    20,    18,     0,    24
   };
   };
 
 
-  const unsigned char
+  const signed char
   EvalParser::yycheck_[] =
   EvalParser::yycheck_[] =
   {
   {
-       6,     6,     8,     0,     4,    11,     5,    13,     8,     9,
-       3,     5,     7
+       4,     5,    10,    10,    12,     6,     8,     0,    12,     3,
+      14,    15,    13,    13,    11,     7,     7,    13,    -1,     9
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yystos_[] =
   EvalParser::yystos_[] =
   {
   {
-       0,     4,     8,     9,    11,    12,     6,     0,     3,    12,
-      12,     5,    12,     5,    12,     7
+       0,     4,     5,    12,    14,    15,    17,    18,    19,    10,
+       8,     0,     3,    13,    19,    19,    11,     7,    13,    20,
+       7,     6,    13,    21,     9
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yyr1_[] =
   EvalParser::yyr1_[] =
   {
   {
-       0,    10,    11,    11,    12,    12,    12
+       0,    16,    17,    18,    19,    19,    19,    19,    19,    20,
+      21,    21
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yyr2_[] =
   EvalParser::yyr2_[] =
   {
   {
-       0,     2,     3,     1,     1,     1,     8
+       0,     2,     1,     3,     1,     1,     4,     8,     1,     1,
+       1,     1
   };
   };
 
 
 
 
@@ -924,16 +981,19 @@ namespace isc { namespace eval {
   const char*
   const char*
   const EvalParser::yytname_[] =
   const EvalParser::yytname_[] =
   {
   {
-  "\"end of file\"", "error", "$undefined", "\"==\"", "\"substring\"",
-  "\",\"", "\"(\"", "\")\"", "\"constant string\"", "\"option code\"",
-  "$accept", "expression", "token", YY_NULLPTR
+  "\"end of file\"", "error", "$undefined", "\"==\"", "\"option\"",
+  "\"substring\"", "\"all\"", "\",\"", "\"(\"", "\")\"", "\"[\"", "\"]\"",
+  "\"constant string\"", "\"integer\"", "\"constant hexstring\"", "TOKEN",
+  "$accept", "expression", "bool_expr", "string_expr", "start_expr",
+  "length_expr", YY_NULLPTR
   };
   };
 
 
 #if YYDEBUG
 #if YYDEBUG
   const unsigned char
   const unsigned char
   EvalParser::yyrline_[] =
   EvalParser::yyrline_[] =
   {
   {
-       0,    64,    64,    68,    71,    75,    79
+       0,    69,    69,    72,    79,    84,    89,   107,   112,   116,
+     123,   128
   };
   };
 
 
   // Print the state stack on the debug stream.
   // Print the state stack on the debug stream.
@@ -968,12 +1028,12 @@ namespace isc { namespace eval {
 
 
 #line 21 "parser.yy" // lalr1.cc:1167
 #line 21 "parser.yy" // lalr1.cc:1167
 } } // isc::eval
 } } // isc::eval
-#line 972 "parser.cc" // lalr1.cc:1167
-#line 83 "parser.yy" // lalr1.cc:1168
+#line 1032 "parser.cc" // lalr1.cc:1167
+#line 135 "parser.yy" // lalr1.cc:1168
 
 
 void
 void
-isc::eval::EvalParser::error(const location_type& l,
-                             const std::string& m)
+isc::eval::EvalParser::error(const location_type& loc,
+                             const std::string& what)
 {
 {
-    ctx.error(l, m);
+    ctx.error(loc, what);
 }
 }

+ 111 - 54
src/lib/eval/parser.h

@@ -44,11 +44,13 @@
 
 
 #include <string>
 #include <string>
 #include <eval/token.h>
 #include <eval/token.h>
-class EvalContext;
+#include <eval/eval_context_decl.h>
+#include <boost/lexical_cast.hpp>
 
 
 using namespace isc::dhcp;
 using namespace isc::dhcp;
+using namespace isc::eval;
 
 
-#line 52 "parser.h" // lalr1.cc:392
+#line 54 "parser.h" // lalr1.cc:392
 
 
 # include <cassert>
 # include <cassert>
 # include <cstdlib> // std::abort
 # include <cstdlib> // std::abort
@@ -125,7 +127,7 @@ using namespace isc::dhcp;
 
 
 #line 21 "parser.yy" // lalr1.cc:392
 #line 21 "parser.yy" // lalr1.cc:392
 namespace isc { namespace eval {
 namespace isc { namespace eval {
-#line 129 "parser.h" // lalr1.cc:392
+#line 131 "parser.h" // lalr1.cc:392
 
 
 
 
 
 
@@ -292,11 +294,11 @@ 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 code"
-      char dummy1[sizeof(int)];
-
       // "constant string"
       // "constant string"
-      char dummy2[sizeof(std::string)];
+      // "integer"
+      // "constant hexstring"
+      // TOKEN
+      char dummy1[sizeof(std::string)];
 };
 };
 
 
     /// Symbol semantic values.
     /// Symbol semantic values.
@@ -321,12 +323,18 @@ namespace isc { namespace eval {
       {
       {
         TOKEN_END = 0,
         TOKEN_END = 0,
         TOKEN_EQUAL = 258,
         TOKEN_EQUAL = 258,
-        TOKEN_SUBSTRING = 259,
-        TOKEN_COMA = 260,
-        TOKEN_LPAREN = 261,
-        TOKEN_RPAREN = 262,
-        TOKEN_STRING = 263,
-        TOKEN_OPTION = 264
+        TOKEN_OPTION = 259,
+        TOKEN_SUBSTRING = 260,
+        TOKEN_ALL = 261,
+        TOKEN_COMA = 262,
+        TOKEN_LPAREN = 263,
+        TOKEN_RPAREN = 264,
+        TOKEN_LBRACKET = 265,
+        TOKEN_RBRACKET = 266,
+        TOKEN_STRING = 267,
+        TOKEN_INTEGER = 268,
+        TOKEN_HEXSTRING = 269,
+        TOKEN_TOKEN = 270
       };
       };
     };
     };
 
 
@@ -364,8 +372,6 @@ 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 int 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);
 
 
 
 
@@ -445,10 +451,18 @@ namespace isc { namespace eval {
 
 
     static inline
     static inline
     symbol_type
     symbol_type
+    make_OPTION (const location_type& l);
+
+    static inline
+    symbol_type
     make_SUBSTRING (const location_type& l);
     make_SUBSTRING (const location_type& l);
 
 
     static inline
     static inline
     symbol_type
     symbol_type
+    make_ALL (const location_type& l);
+
+    static inline
+    symbol_type
     make_COMA (const location_type& l);
     make_COMA (const location_type& l);
 
 
     static inline
     static inline
@@ -461,11 +475,27 @@ namespace isc { namespace eval {
 
 
     static inline
     static inline
     symbol_type
     symbol_type
+    make_LBRACKET (const location_type& l);
+
+    static inline
+    symbol_type
+    make_RBRACKET (const location_type& l);
+
+    static inline
+    symbol_type
     make_STRING (const std::string& v, const location_type& l);
     make_STRING (const std::string& v, const location_type& l);
 
 
     static inline
     static inline
     symbol_type
     symbol_type
-    make_OPTION (const int& v, const location_type& l);
+    make_INTEGER (const std::string& v, const location_type& l);
+
+    static inline
+    symbol_type
+    make_HEXSTRING (const std::string& v, const location_type& l);
+
+    static inline
+    symbol_type
+    make_TOKEN (const std::string& v, const location_type& l);
 
 
 
 
     /// Build a parser object.
     /// Build a parser object.
@@ -552,7 +582,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.
@@ -672,12 +702,12 @@ namespace isc { namespace eval {
     enum
     enum
     {
     {
       yyeof_ = 0,
       yyeof_ = 0,
-      yylast_ = 12,     ///< Last index in yytable_.
-      yynnts_ = 3,  ///< Number of nonterminal symbols.
-      yyfinal_ = 7, ///< Termination state number.
+      yylast_ = 19,     ///< Last index in yytable_.
+      yynnts_ = 6,  ///< Number of nonterminal symbols.
+      yyfinal_ = 11, ///< Termination state number.
       yyterror_ = 1,
       yyterror_ = 1,
       yyerrcode_ = 256,
       yyerrcode_ = 256,
-      yyntokens_ = 10  ///< Number of tokens.
+      yyntokens_ = 16  ///< Number of tokens.
     };
     };
 
 
 
 
@@ -720,9 +750,10 @@ 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,     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
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15
     };
     };
-    const unsigned int user_token_number_max_ = 264;
+    const unsigned int user_token_number_max_ = 270;
     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_)
@@ -755,11 +786,10 @@ namespace isc { namespace eval {
   {
   {
       switch (other.type_get ())
       switch (other.type_get ())
     {
     {
-      case 9: // "option code"
-        value.copy< int > (other.value);
-        break;
-
-      case 8: // "constant string"
+      case 12: // "constant string"
+      case 13: // "integer"
+      case 14: // "constant hexstring"
+      case 15: // TOKEN
         value.copy< std::string > (other.value);
         value.copy< std::string > (other.value);
         break;
         break;
 
 
@@ -780,11 +810,10 @@ namespace isc { namespace eval {
     (void) v;
     (void) v;
       switch (this->type_get ())
       switch (this->type_get ())
     {
     {
-      case 9: // "option code"
-        value.copy< int > (v);
-        break;
-
-      case 8: // "constant string"
+      case 12: // "constant string"
+      case 13: // "integer"
+      case 14: // "constant hexstring"
+      case 15: // TOKEN
         value.copy< std::string > (v);
         value.copy< std::string > (v);
         break;
         break;
 
 
@@ -804,13 +833,6 @@ namespace isc { namespace eval {
   {}
   {}
 
 
   template <typename Base>
   template <typename Base>
-  EvalParser::basic_symbol<Base>::basic_symbol (typename Base::kind_type t, const int 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)
@@ -843,11 +865,10 @@ namespace isc { namespace eval {
     // Type destructor.
     // Type destructor.
     switch (yytype)
     switch (yytype)
     {
     {
-      case 9: // "option code"
-        value.template destroy< int > ();
-        break;
-
-      case 8: // "constant string"
+      case 12: // "constant string"
+      case 13: // "integer"
+      case 14: // "constant hexstring"
+      case 15: // TOKEN
         value.template destroy< std::string > ();
         value.template destroy< std::string > ();
         break;
         break;
 
 
@@ -874,11 +895,10 @@ namespace isc { namespace eval {
     super_type::move(s);
     super_type::move(s);
       switch (this->type_get ())
       switch (this->type_get ())
     {
     {
-      case 9: // "option code"
-        value.move< int > (s.value);
-        break;
-
-      case 8: // "constant string"
+      case 12: // "constant string"
+      case 13: // "integer"
+      case 14: // "constant hexstring"
+      case 15: // TOKEN
         value.move< std::string > (s.value);
         value.move< std::string > (s.value);
         break;
         break;
 
 
@@ -937,7 +957,8 @@ namespace isc { namespace eval {
     const unsigned short int
     const unsigned short int
     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
     };
     };
     return static_cast<token_type> (yytoken_number_[type]);
     return static_cast<token_type> (yytoken_number_[type]);
   }
   }
@@ -955,12 +976,24 @@ namespace isc { namespace eval {
   }
   }
 
 
   EvalParser::symbol_type
   EvalParser::symbol_type
+  EvalParser::make_OPTION (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_OPTION, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_SUBSTRING (const location_type& l)
   EvalParser::make_SUBSTRING (const location_type& l)
   {
   {
     return symbol_type (token::TOKEN_SUBSTRING, l);
     return symbol_type (token::TOKEN_SUBSTRING, l);
   }
   }
 
 
   EvalParser::symbol_type
   EvalParser::symbol_type
+  EvalParser::make_ALL (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_ALL, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_COMA (const location_type& l)
   EvalParser::make_COMA (const location_type& l)
   {
   {
     return symbol_type (token::TOKEN_COMA, l);
     return symbol_type (token::TOKEN_COMA, l);
@@ -979,21 +1012,45 @@ namespace isc { namespace eval {
   }
   }
 
 
   EvalParser::symbol_type
   EvalParser::symbol_type
+  EvalParser::make_LBRACKET (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_LBRACKET, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_RBRACKET (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_RBRACKET, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_STRING (const std::string& v, const location_type& l)
   EvalParser::make_STRING (const std::string& v, const location_type& l)
   {
   {
     return symbol_type (token::TOKEN_STRING, v, l);
     return symbol_type (token::TOKEN_STRING, v, l);
   }
   }
 
 
   EvalParser::symbol_type
   EvalParser::symbol_type
-  EvalParser::make_OPTION (const int& v, const location_type& l)
+  EvalParser::make_INTEGER (const std::string& v, const location_type& l)
+  {
+    return symbol_type (token::TOKEN_INTEGER, v, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_HEXSTRING (const std::string& v, const location_type& l)
+  {
+    return symbol_type (token::TOKEN_HEXSTRING, v, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_TOKEN (const std::string& v, const location_type& l)
   {
   {
-    return symbol_type (token::TOKEN_OPTION, v, l);
+    return symbol_type (token::TOKEN_TOKEN, v, l);
   }
   }
 
 
 
 
 #line 21 "parser.yy" // lalr1.cc:392
 #line 21 "parser.yy" // lalr1.cc:392
 } } // isc::eval
 } } // isc::eval
-#line 997 "parser.h" // lalr1.cc:392
+#line 1054 "parser.h" // lalr1.cc:392
 
 
 
 
 
 

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

@@ -24,18 +24,15 @@
 {
 {
 #include <string>
 #include <string>
 #include <eval/token.h>
 #include <eval/token.h>
-class EvalContext;
+#include <eval/eval_context_decl.h>
+#include <boost/lexical_cast.hpp>
 
 
 using namespace isc::dhcp;
 using namespace isc::dhcp;
+using namespace isc::eval;
 }
 }
 // The parsing context.
 // The parsing context.
 %param { EvalContext& ctx }
 %param { EvalContext& ctx }
 %locations
 %locations
-%initial-action
-{
-  // Initialize the initial location.
-  @$.begin.filename = @$.end.filename = &ctx.file;
-};
 %define parse.trace
 %define parse.trace
 %define parse.error verbose
 %define parse.error verbose
 %code
 %code
@@ -46,13 +43,21 @@ using namespace isc::dhcp;
 %token
 %token
   END  0  "end of file"
   END  0  "end of file"
   EQUAL "=="
   EQUAL "=="
+  OPTION "option"
   SUBSTRING "substring"
   SUBSTRING "substring"
+  ALL "all"
   COMA ","
   COMA ","
   LPAREN  "("
   LPAREN  "("
   RPAREN  ")"
   RPAREN  ")"
+  LBRACKET "["
+  RBRACKET "]"
 ;
 ;
+
 %token <std::string> STRING "constant string"
 %token <std::string> STRING "constant string"
-%token <int> OPTION "option code"
+%token <std::string> INTEGER "integer"
+%token <std::string> HEXSTRING "constant hexstring"
+%token <std::string> TOKEN
+
 %printer { yyoutput << $$; } <*>;
 %printer { yyoutput << $$; } <*>;
 %%
 %%
 
 
@@ -60,30 +65,77 @@ using namespace isc::dhcp;
 %start expression;
 %start expression;
 
 
 // Expression can either be a single token or a (something == something) expression
 // Expression can either be a single token or a (something == something) expression
-expression:
-token EQUAL token {
-    TokenPtr eq(new TokenEqual());
-    ctx.expression.push_back(eq);
-}
-| token;
 
 
-token:
-STRING {
-    TokenPtr str(new TokenString($1));
-    ctx.expression.push_back(str);
-}
-| OPTION {
-    TokenPtr opt(new TokenOption($1));
-    ctx.expression.push_back(opt);
-}
-| SUBSTRING "(" token "," token "," token ")" {
-    /* push back TokenSubstring */
-  }
+expression : bool_expr
+           ;
+
+bool_expr : string_expr EQUAL string_expr
+                {
+                    TokenPtr eq(new TokenEqual());
+                    ctx.expression.push_back(eq);
+                }
+          ;
+
+string_expr : STRING
+                  {
+                      TokenPtr str(new TokenString($1));
+                      ctx.expression.push_back(str);
+                  }
+            | HEXSTRING
+                  {
+                      TokenPtr hex(new TokenHexString($1));
+                      ctx.expression.push_back(hex);
+                  }
+            | OPTION "[" INTEGER "]"
+                  {
+                      int n;
+                      try {
+                          n  = boost::lexical_cast<int>($3);
+                      } catch (const boost::bad_lexical_cast &) {
+                          // This can't happen...
+                          ctx.error(@3,
+                                    "Option code has invalid value in " + $3);
+                      }
+                      if (n < 0 || n > 65535) {
+                          ctx.error(@3,
+                                    "Option code has invalid value in "
+                                    + $3 + ". Allowed range: 0..65535");
+                      }
+                      TokenPtr opt(new TokenOption(static_cast<uint16_t>(n)));
+                      ctx.expression.push_back(opt);
+                  }
+            | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
+                  {
+                      TokenPtr sub(new TokenSubstring());
+                      ctx.expression.push_back(sub);
+                  }
+            | TOKEN
+                // Temporary unused token to avoid explict but long errors
+            ;
+
+start_expr : INTEGER
+                 {
+                     TokenPtr str(new TokenString($1));
+                     ctx.expression.push_back(str);
+                 }
+           ;
+
+length_expr : INTEGER
+                  {
+                      TokenPtr str(new TokenString($1));
+                      ctx.expression.push_back(str);
+                  }
+            | ALL
+                 {
+                     TokenPtr str(new TokenString("all"));
+                     ctx.expression.push_back(str);
+                 }
+            ;
 
 
 %%
 %%
 void
 void
-isc::eval::EvalParser::error(const location_type& l,
-                             const std::string& m)
+isc::eval::EvalParser::error(const location_type& loc,
+                             const std::string& what)
 {
 {
-    ctx.error(l, m);
+    ctx.error(loc, what);
 }
 }

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

@@ -1,3 +1,4 @@
+// Generated 2015114
 // 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++

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

@@ -1,3 +1,4 @@
+// Generated 2015114
 // 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++

+ 211 - 9
src/lib/eval/tests/context_unittest.cc

@@ -27,8 +27,10 @@ using namespace isc::dhcp;
 
 
 namespace {
 namespace {
 
 
+/// @brief Test class for testing EvalContext aka class test parsing
 class EvalContextTest : public ::testing::Test {
 class EvalContextTest : public ::testing::Test {
 public:
 public:
+    /// @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);
         boost::shared_ptr<TokenString> str =
         boost::shared_ptr<TokenString> str =
@@ -45,6 +47,25 @@ public:
         EXPECT_EQ(expected, values.top());
         EXPECT_EQ(expected, values.top());
     }
     }
 
 
+    /// @brief checks if the given token is a hex string with the expected value
+    void checkTokenHexString(const TokenPtr& token,
+                             const std::string& expected) {
+        ASSERT_TRUE(token);
+        boost::shared_ptr<TokenHexString> hex =
+            boost::dynamic_pointer_cast<TokenHexString>(token);
+        ASSERT_TRUE(hex);
+
+        Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
+        ValueStack values;
+
+        EXPECT_NO_THROW(token->evaluate(*pkt4, values));
+
+        ASSERT_EQ(1, values.size());
+
+        EXPECT_EQ(expected, values.top());
+    }
+
+    /// @brief checks if the given token is an equal operator
     void checkTokenEq(const TokenPtr& token) {
     void checkTokenEq(const TokenPtr& token) {
         ASSERT_TRUE(token);
         ASSERT_TRUE(token);
         boost::shared_ptr<TokenEqual> eq =
         boost::shared_ptr<TokenEqual> eq =
@@ -52,39 +73,115 @@ public:
         EXPECT_TRUE(eq);
         EXPECT_TRUE(eq);
     }
     }
 
 
-    void checkTokenOption(const TokenPtr& token, uint16_t expected_option) {
+    /// @brief checks if the given token is an option with the expected code
+    void checkTokenOption(const TokenPtr& token, uint16_t expected_code) {
         ASSERT_TRUE(token);
         ASSERT_TRUE(token);
         boost::shared_ptr<TokenOption> opt =
         boost::shared_ptr<TokenOption> opt =
             boost::dynamic_pointer_cast<TokenOption>(token);
             boost::dynamic_pointer_cast<TokenOption>(token);
         ASSERT_TRUE(opt);
         ASSERT_TRUE(opt);
 
 
-        EXPECT_EQ(expected_option, opt->getCode());
+        EXPECT_EQ(expected_code, opt->getCode());
     }
     }
+
+    /// @brief checks if the given token is a substring operator
+    void checkTokenSubstring(const TokenPtr& token) {
+        ASSERT_TRUE(token);
+        boost::shared_ptr<TokenSubstring> sub =
+            boost::dynamic_pointer_cast<TokenSubstring>(token);
+        EXPECT_TRUE(sub);
+    }
+
+    /// @brief checks if the given expression raises the expected message
+    /// when it is parsed.
+    void checkError(const string& expr, const string& msg) {
+        EvalContext eval;
+        parsed_ = false;
+        try {
+            parsed_ = eval.parseString(expr);
+            FAIL() << "Expected EvalParseError but nothing was raised";
+        }
+        catch (const EvalParseError& ex) {
+            EXPECT_EQ(msg, ex.what());
+            EXPECT_FALSE(parsed_);
+        }
+        catch (...) {
+            FAIL() << "Expected EvalParseError but something else was raised";
+        }
+    }
+
+    bool parsed_; ///< Parsing status
 };
 };
 
 
+// Test the parsing of a basic expression
 TEST_F(EvalContextTest, basic) {
 TEST_F(EvalContextTest, basic) {
 
 
     EvalContext tmp;
     EvalContext tmp;
 
 
-    EXPECT_NO_THROW(tmp.parseString("option[123] == 'MSFT'"));
+    EXPECT_NO_THROW(parsed_ = tmp.parseString("option[123] == 'MSFT'"));
+    EXPECT_TRUE(parsed_);
 }
 }
 
 
+// Test the parsing of a string terminal
 TEST_F(EvalContextTest, string) {
 TEST_F(EvalContextTest, string) {
     EvalContext eval;
     EvalContext eval;
 
 
-    EXPECT_NO_THROW(eval.parseString("'foo'"));
+    EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
+    EXPECT_TRUE(parsed_);
+
+    ASSERT_EQ(3, eval.expression.size());
+
+    TokenPtr tmp1  = eval.expression.at(0);
+    TokenPtr tmp2  = eval.expression.at(1);
+
+    checkTokenString(tmp1, "foo");
+    checkTokenString(tmp2, "bar");
+}
+
+// Test the parsing of a basic expression using integers
+TEST_F(EvalContextTest, integer) {
+
+    EvalContext eval;
+
+    EXPECT_NO_THROW(parsed_ =
+        eval.parseString("substring(option[123], 0, 2) == '42'"));
+    EXPECT_TRUE(parsed_);
+}
+
+// Test the parsing of a hexstring terminal
+TEST_F(EvalContextTest, hexstring) {
+    EvalContext eval;
+
+    EXPECT_NO_THROW(parsed_ = eval.parseString("0x666f6f == 'foo'"));
+    EXPECT_TRUE(parsed_);
+
+    ASSERT_EQ(3, eval.expression.size());
+
+    TokenPtr tmp = eval.expression.at(0);
+
+    checkTokenHexString(tmp, "foo");
+}
+
+// Test the parsing of a hexstring terminal with an odd number of
+// hexadecimal digits
+TEST_F(EvalContextTest, oddHexstring) {
+    EvalContext eval;
+
+    EXPECT_NO_THROW(parsed_ = eval.parseString("0X7 == 'foo'"));
+    EXPECT_TRUE(parsed_);
 
 
-    ASSERT_EQ(1, eval.expression.size());
+    ASSERT_EQ(3, eval.expression.size());
 
 
     TokenPtr tmp = eval.expression.at(0);
     TokenPtr tmp = eval.expression.at(0);
 
 
-    checkTokenString(tmp, "foo");
+    checkTokenHexString(tmp, "\a");
 }
 }
 
 
+// Test the parsing of an equal expression
 TEST_F(EvalContextTest, equal) {
 TEST_F(EvalContextTest, equal) {
     EvalContext eval;
     EvalContext eval;
 
 
-    EXPECT_NO_THROW(eval.parseString("'foo' == 'bar'"));
+    EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
+    EXPECT_TRUE(parsed_);
 
 
     ASSERT_EQ(3, eval.expression.size());
     ASSERT_EQ(3, eval.expression.size());
 
 
@@ -97,12 +194,117 @@ TEST_F(EvalContextTest, equal) {
     checkTokenEq(tmp3);
     checkTokenEq(tmp3);
 }
 }
 
 
+// Test the parsing of an option terminal
 TEST_F(EvalContextTest, option) {
 TEST_F(EvalContextTest, option) {
     EvalContext eval;
     EvalContext eval;
 
 
-    EXPECT_NO_THROW(eval.parseString("option[123]"));
-    ASSERT_EQ(1, eval.expression.size());
+    EXPECT_NO_THROW(parsed_ = eval.parseString("option[123] == 'foo'"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(3, eval.expression.size());
     checkTokenOption(eval.expression.at(0), 123);
     checkTokenOption(eval.expression.at(0), 123);
 }
 }
 
 
+// Test the parsing of a substring expression
+TEST_F(EvalContextTest, substring) {
+    EvalContext eval;
+
+    EXPECT_NO_THROW(parsed_ =
+        eval.parseString("substring('foobar',2,all) == 'obar'"));
+    EXPECT_TRUE(parsed_);
+
+    ASSERT_EQ(6, eval.expression.size());
+
+    TokenPtr tmp1 = eval.expression.at(0);
+    TokenPtr tmp2 = eval.expression.at(1);
+    TokenPtr tmp3 = eval.expression.at(2);
+    TokenPtr tmp4 = eval.expression.at(3);
+
+    checkTokenString(tmp1, "foobar");
+    checkTokenString(tmp2, "2");
+    checkTokenString(tmp3, "all");
+    checkTokenSubstring(tmp4);
+}
+
+// Test some scanner error cases
+TEST_F(EvalContextTest, scanErrors) {
+    checkError("'", "<string>:1.1: Invalid character: '");
+    checkError("'\''", "<string>:1.3: Invalid character: '");
+    checkError("'\n'", "<string>:1.1: Invalid character: '");
+    checkError("0x123h", "<string>:1.6: Invalid character: h");
+    checkError("=", "<string>:1.1: Invalid character: =");
+    checkError("subtring", "<string>:1.1: Invalid character: s");
+    checkError("foo", "<string>:1.1: Invalid character: f");
+    checkError(" bar", "<string>:1.2: Invalid character: b");
+}
+
+// Tests some scanner/parser error cases
+TEST_F(EvalContextTest, scanParseErrors) {
+    checkError("", "<string>:1.1: syntax error, unexpected end of file");
+    checkError(" ", "<string>:1.2: syntax error, unexpected end of file");
+    checkError("0x", "<string>:1.1: syntax error, unexpected integer");
+    checkError("0abc",
+               "<string>:1.1: syntax error, unexpected integer");
+    checkError("===", "<string>:1.1-2: syntax error, unexpected ==");
+    checkError("option[-1]",
+               "<string>:1.8-9: Option code has invalid "
+               "value in -1. Allowed range: 0..65535");
+    checkError("option[65536]",
+               "<string>:1.8-12: Option code has invalid "
+               "value in 65536. Allowed range: 0..65535");
+    checkError("option[12345678901234567890]",
+               "<string>:1.8-27: Failed to convert 12345678901234567890 "
+               "to an integer.");
+    checkError("option[123] < 'foo'", "<string>:1.13: Invalid character: <");
+    checkError("substring('foo',12345678901234567890,1)",
+               "<string>:1.17-36: Failed to convert 12345678901234567890 "
+               "to an integer.");
+}
+
+// Tests some parser error cases
+TEST_F(EvalContextTest, parseErrors) {
+    checkError("'foo''bar'",
+               "<string>:1.6-10: syntax error, unexpected constant string, "
+               "expecting ==");
+    checkError("== 'ab'", "<string>:1.1-2: syntax error, unexpected ==");
+    checkError("'foo' ==",
+               "<string>:1.9: syntax error, unexpected end of file");
+    checkError("option 'ab'",
+               "<string>:1.8-11: syntax error, unexpected "
+               "constant string, expecting [");
+    checkError("option(10) == 'ab'",
+               "<string>:1.7: syntax error, "
+               "unexpected (, expecting [");
+    checkError("option['ab'] == 'foo'",
+               "<string>:1.8-11: syntax error, "
+               "unexpected constant string, "
+               "expecting integer");
+    checkError("option[0xa] == 'ab'",
+               "<string>:1.8-10: syntax error, "
+               "unexpected constant hexstring, "
+               "expecting integer");
+    checkError("substring('foobar') == 'f'",
+               "<string>:1.19: syntax error, "
+               "unexpected ), expecting \",\"");
+    checkError("substring('foobar',3) == 'bar'",
+               "<string>:1.21: syntax error, unexpected ), expecting \",\"");
+    checkError("substring('foobar','3',3) == 'bar'",
+               "<string>:1.20-22: syntax error, unexpected constant string, "
+               "expecting integer");
+    checkError("substring('foobar',1,a) == 'foo'",
+               "<string>:1.22: Invalid character: a");
+}
+
+// Tests some type error cases (caught only by the strongly typed parser)
+TEST_F(EvalContextTest, typeErrors) {
+    checkError("'foobar'",
+               "<string>:1.9: syntax error, unexpected end of file, "
+               "expecting ==");
+    checkError("substring('foobar',all,1) == 'foo'",
+               "<string>:1.20-22: syntax error, unexpected all, "
+               "expecting integer");
+    checkError("substring('foobar',0x32,1) == 'foo'",
+               "<string>:1.20-23: syntax error, unexpected constant "
+               "hexstring, expecting integer");
+}
+
 };
 };

+ 23 - 16
src/lib/eval/tests/token_unittest.cc

@@ -72,10 +72,12 @@ public:
     /// @param test_start The postion to start when getting a substring
     /// @param test_start The postion to start when getting a substring
     /// @param test_length The length of the substring to get
     /// @param test_length The length of the substring to get
     /// @param result_string The expected result of the eval
     /// @param result_string The expected result of the eval
+    /// @param should_throw The eval will throw
     void verifySubstringEval(const std::string& test_string,
     void verifySubstringEval(const std::string& test_string,
                              const std::string& test_start,
                              const std::string& test_start,
                              const std::string& test_length,
                              const std::string& test_length,
-                             const std::string& result_string) {
+                             const std::string& result_string,
+                             bool should_throw = false) {
 
 
         // create the token
         // create the token
         ASSERT_NO_THROW(t_.reset(new TokenSubstring()));
         ASSERT_NO_THROW(t_.reset(new TokenSubstring()));
@@ -86,14 +88,19 @@ public:
         values_.push(test_length);
         values_.push(test_length);
 
 
         // evaluate the token
         // evaluate the token
-        EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
-
-        // verify results
-        ASSERT_EQ(1, values_.size());
-        EXPECT_EQ(result_string, values_.top());
-
-        // remove result
-        values_.pop();
+        if (should_throw) {
+            EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
+            ASSERT_EQ(0, values_.size());
+        } else {
+            EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+            // verify results
+            ASSERT_EQ(1, values_.size());
+            EXPECT_EQ(result_string, values_.top());
+
+            // remove result
+            values_.pop();
+        }
     }
     }
 
 
     /// @todo: Add more option types here
     /// @todo: Add more option types here
@@ -443,13 +450,13 @@ TEST_F(TokenTest, substringStartingPosition) {
 // Check what happens if we use strings that aren't numbers for start or length
 // Check what happens if we use strings that aren't numbers for start or length
 // We should return the empty string
 // We should return the empty string
 TEST_F(TokenTest, substringBadParams) {
 TEST_F(TokenTest, substringBadParams) {
-    verifySubstringEval("foobar", "0ick", "all", "");
-    verifySubstringEval("foobar", "ick0", "all", "");
-    verifySubstringEval("foobar", "ick", "all", "");
-    verifySubstringEval("foobar", "0", "ick", "");
-    verifySubstringEval("foobar", "0", "0ick", "");
-    verifySubstringEval("foobar", "0", "ick0", "");
-    verifySubstringEval("foobar", "0", "allaboard", "");
+    verifySubstringEval("foobar", "0ick", "all", "", true);
+    verifySubstringEval("foobar", "ick0", "all", "", true);
+    verifySubstringEval("foobar", "ick", "all", "", true);
+    verifySubstringEval("foobar", "0", "ick", "", true);
+    verifySubstringEval("foobar", "0", "0ick", "", true);
+    verifySubstringEval("foobar", "0", "ick0", "", true);
+    verifySubstringEval("foobar", "0", "allaboard", "", true);
 }
 }
 
 
 // lastly check that we don't get anything if the string is empty or
 // lastly check that we don't get anything if the string is empty or

+ 9 - 7
src/lib/eval/token.cc

@@ -121,19 +121,21 @@ TokenSubstring::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
     int length;
     int length;
     try {
     try {
         start_pos = boost::lexical_cast<int>(start_str);
         start_pos = boost::lexical_cast<int>(start_str);
+    } catch (const boost::bad_lexical_cast&) {
+        isc_throw(EvalTypeError, "the parameter '" << start_str
+                  << "' for the starting postion of the substring "
+                  << "couldn't be converted to an integer.");
+    }
+    try {
         if (len_str == "all") {
         if (len_str == "all") {
             length = string_str.length();
             length = string_str.length();
         } else {
         } else {
             length = boost::lexical_cast<int>(len_str);
             length = boost::lexical_cast<int>(len_str);
         }
         }
     } catch (const boost::bad_lexical_cast&) {
     } catch (const boost::bad_lexical_cast&) {
-        LOG_DEBUG(eval_logger, EVAL_DBG_TRACE,
-                  EVAL_SUBSTRING_BAD_PARAM_CONVERSION)
-            .arg(start_str)
-            .arg(len_str);
-
-        values.push("");
-        return;
+        isc_throw(EvalTypeError, "the parameter '" << len_str
+                  << "' for the length of the substring "
+                  << "couldn't be converted to an integer.");
     }
     }
 
 
     const int string_length = string_str.length();
     const int string_length = string_str.length();

+ 11 - 1
src/lib/eval/token.h

@@ -38,7 +38,7 @@ typedef std::vector<TokenPtr> Expression;
 /// Evaluated values are stored as a stack of strings
 /// Evaluated values are stored as a stack of strings
 typedef std::stack<std::string> ValueStack;
 typedef std::stack<std::string> ValueStack;
 
 
-/// @brief EvalStackError is thrown when more or less parameters are on the
+/// @brief EvalBadStack is thrown when more or less parameters are on the
 ///        stack than expected.
 ///        stack than expected.
 class EvalBadStack : public Exception {
 class EvalBadStack : public Exception {
 public:
 public:
@@ -46,6 +46,14 @@ public:
         isc::Exception(file, line, what) { };
         isc::Exception(file, line, what) { };
 };
 };
 
 
+/// @brief EvalTypeError is thrown when a value on the stack has a content
+///        with an unexpected type.
+class EvalTypeError : public Exception {
+public:
+    EvalTypeError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 /// @brief Base class for all tokens
 /// @brief Base class for all tokens
 ///
 ///
 /// It provides an interface for all tokens and storage for string representation
 /// It provides an interface for all tokens and storage for string representation
@@ -241,6 +249,8 @@ public:
     /// - -1, -4  => "ooba"
     /// - -1, -4  => "ooba"
     ///
     ///
     /// @throw EvalBadStack if there are less than 3 values on stack
     /// @throw EvalBadStack if there are less than 3 values on stack
+    /// @throw EvalTypeError if start is not a number or length a number or
+    ///        the special value "all".
     ///
     ///
     /// @param pkt (unused)
     /// @param pkt (unused)
     /// @param values - stack of values (3 arguments will be popped, 1 result
     /// @param values - stack of values (3 arguments will be popped, 1 result