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 += 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
 

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

@@ -22,9 +22,8 @@
   classification, but in the future may be also used for other applications.
 
   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
   tokens that are stored in Reverse Polish Notation in
   EvalContext::expression.
@@ -66,27 +65,35 @@
 @code
 01. %start expression;
 02.
-03. expression:
-04. token EQUAL token
-05. | token;
+03. expression : token EQUAL token
+04.            | token
+05.            ;
 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
 
 This code determines that the grammar starts from expression (line 1).
 The actual definition of expression (lines 3-5) may either be a
 single token or an expression "token == token" (EQUAL has been defined as
 "==" 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
 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);
     parser.set_debug_level(trace_parsing_);
     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
-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
-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 <map>
 #include <eval/parser.h>
+#include <eval/eval_context_decl.h>
 #include <exceptions/exceptions.h>
 
 // Tell Flex the lexer's prototype ...
@@ -25,10 +26,13 @@
 // ... and declare it for the parser's sake.
 YY_DECL;
 
+namespace isc {
+namespace eval {
+
 /// @brief Evaluation error exception raised when trying to parse an axceptions.
-class EvalError : public isc::Exception {
+class EvalParseError : public isc::Exception {
 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) { };
 };
 
@@ -46,32 +50,30 @@ public:
     /// @brief Parsed expression (output tokens are stored here)
     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.
     ///
     /// @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.
     /// 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
     ///
-    /// @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.
-    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
     ///
@@ -87,4 +89,8 @@ public:
     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
 to said value. This message is mostly useful during debugging of the
 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 YY_FLEX_MAJOR_VERSION 2
 #define YY_FLEX_MINOR_VERSION 5
-#define YY_FLEX_SUBMINOR_VERSION 37
+#define YY_FLEX_SUBMINOR_VERSION 39
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #define FLEX_BETA
 #endif
@@ -230,6 +230,13 @@ extern FILE *yyin, *yyout;
                     if ( yytext[yyl] == '\n' )\
                         --yylineno;\
             }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. */
 #define yyless(n) \
@@ -438,6 +445,8 @@ int yylineno = 1;
 extern char *yytext;
 #define yytext_ptr yytext
 
+/* %% [1.5] DFA */
+
 /* %if-c-only Standard (non-C++) definition */
 
 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;
 
 /* %% [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,
    but its presence is necessary. */
 struct yy_trans_info
@@ -469,12 +478,13 @@ struct yy_trans_info
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static yyconst flex_int16_t yy_accept[36] =
+static yyconst flex_int16_t yy_accept[44] =
     {   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] =
@@ -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,    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,
@@ -509,57 +519,61 @@ static yyconst flex_int32_t yy_ec[256] =
         1,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int32_t yy_meta[23] =
+static yyconst flex_int32_t yy_meta[28] =
     {   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,   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,
-       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,
-        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,
         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. */
-static yyconst flex_int32_t yy_rule_can_match_eol[12] =
+static yyconst flex_int32_t yy_rule_can_match_eol[17] =
     {   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 char *yy_last_accepting_cpos;
@@ -567,9 +581,10 @@ static char *yy_last_accepting_cpos;
 extern int yy_flex_debug;
 int yy_flex_debug = 1;
 
-static yyconst flex_int16_t yy_rule_linenum[11] =
+static yyconst flex_int16_t yy_rule_linenum[16] =
     {   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
@@ -636,7 +651,7 @@ static isc::eval::location loc;
 // by moving it ahead by yyleng bytes. yyleng specifies the length of the
 // currently matched token.
 #define YY_USER_ACTION  loc.columns(yyleng);
-#line 640 "lexer.cc"
+#line 655 "lexer.cc"
 
 #define INITIAL 0
 
@@ -875,17 +890,6 @@ YY_DECL
 	register char *yy_cp, *yy_bp;
 	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) )
 		{
 		(yy_init) = 1;
@@ -920,6 +924,18 @@ YY_DECL
 		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 */
 		{
 /* %% [8.0] yymore()-related code goes here */
@@ -938,7 +954,7 @@ YY_DECL
 yy_match:
 		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] )
 				{
 				(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 )
 				{
 				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_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
 			++yy_cp;
 			}
-		while ( yy_current_state != 35 );
+		while ( yy_current_state != 43 );
 		yy_cp = (yy_last_accepting_cpos);
 		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] )
 			{
-			int yyl;
+			yy_size_t yyl;
 			for ( yyl = 0; yyl < yyleng; ++yyl )
 				if ( yytext[yyl] == '\n' )
 					   
@@ -982,13 +998,13 @@ do_action:	/* This label is used only to access EOF actions. */
 			{
 			if ( yy_act == 0 )
 				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",
 				         (long)yy_rule_linenum[yy_act], yytext );
-			else if ( yy_act == 11 )
+			else if ( yy_act == 16 )
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				         yytext );
-			else if ( yy_act == 12 )
+			else if ( yy_act == 17 )
 				fprintf( stderr, "--(end of buffer or a NUL)\n" );
 			else
 				fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
@@ -1039,96 +1055,88 @@ case 4:
 YY_RULE_SETUP
 #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);
 
-    // 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 {
-        n = boost::lexical_cast<int>(tmp);
+        static_cast<void>(boost::lexical_cast<int>(tmp));
     } 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
-case 5:
+case 6:
 YY_RULE_SETUP
-#line 154 "lexer.ll"
+#line 123 "lexer.ll"
 return isc::eval::EvalParser::make_EQUAL(loc);
 	YY_BREAK
-case 6:
+case 7:
 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);
 	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
-#line 156 "lexer.ll"
+#line 127 "lexer.ll"
 return isc::eval::EvalParser::make_LPAREN(loc);
 	YY_BREAK
-case 8:
+case 11:
 YY_RULE_SETUP
-#line 157 "lexer.ll"
+#line 128 "lexer.ll"
 return isc::eval::EvalParser::make_RPAREN(loc);
 	YY_BREAK
-case 9:
+case 12:
 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);
 	YY_BREAK
-case 10:
+case 15:
 YY_RULE_SETUP
-#line 160 "lexer.ll"
+#line 133 "lexer.ll"
 driver.error (loc, "Invalid character: " + std::string(yytext));
 	YY_BREAK
 case YY_STATE_EOF(INITIAL):
-#line 161 "lexer.ll"
+#line 134 "lexer.ll"
 return isc::eval::EvalParser::make_END(loc);
 	YY_BREAK
-case 11:
+case 16:
 YY_RULE_SETUP
-#line 162 "lexer.ll"
+#line 135 "lexer.ll"
 ECHO;
 	YY_BREAK
-#line 1132 "lexer.cc"
+#line 1140 "lexer.cc"
 
 	case YY_END_OF_BUFFER:
 		{
@@ -1259,6 +1267,7 @@ ECHO;
 			"fatal flex scanner internal error--no action found" );
 	} /* end of action switch */
 		} /* end of scanning one token */
+	} /* end of user's declarations */
 } /* end of yylex */
 /* %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 )
 			{
 			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_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 )
 		{
 		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_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;
 }
@@ -1952,7 +1961,7 @@ YY_BUFFER_STATE yy_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len
 	YY_BUFFER_STATE b;
 	char *buf;
 	yy_size_t n;
-	int i;
+	yy_size_t i;
     
 	/* Get memory for full buffer, including space for trailing EOB's. */
 	n = _yybytes_len + 2;
@@ -2215,26 +2224,28 @@ void yyfree (void * ptr )
 
 /* %ok-for-header */
 
-#line 162 "lexer.ll"
+#line 135 "lexer.ll"
+
 
 
+using namespace isc::eval;
 
 void
-EvalContext::scanBegin()
+EvalContext::scanStringBegin()
 {
+    loc.initialize(&file_);
     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);
     }
 }
 
 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
    can be used during actual token definitions. */
-int   [0-9]+
+int   \-?[0-9]+
+hex   [0-9a-fA-F]+
 blank [ \t]
-str [a-zA-Z_0-9]*
 
 %{
 // 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();
 }
 
-\'{str}\' {
+\'[^\'\n]*\' {
     // 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.
     // for 'foo' we should get foo
@@ -100,82 +100,57 @@ str [a-zA-Z_0-9]*
     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 {
-        n = boost::lexical_cast<int>(tmp);
+        static_cast<void>(boost::lexical_cast<int>(tmp));
     } 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);
+"option"    return isc::eval::EvalParser::make_OPTION(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_RPAREN(loc);
+"["         return isc::eval::EvalParser::make_LBRACKET(loc);
+"]"         return isc::eval::EvalParser::make_RBRACKET(loc);
 ","         return isc::eval::EvalParser::make_COMA(loc);
 
 .          driver.error (loc, "Invalid character: " + std::string(yytext));
 <<EOF>>    return isc::eval::EvalParser::make_END(loc);
 %%
 
+using namespace isc::eval;
+
 void
-EvalContext::scanBegin()
+EvalContext::scanStringBegin()
 {
+    loc.initialize(&file_);
     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);
     }
 }
 
 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.
 
 // Locations for Bison parsers in C++

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

@@ -49,7 +49,7 @@
 
 #line 51 "parser.cc" // lalr1.cc:412
 // Unqualified %code blocks.
-#line 42 "parser.yy" // lalr1.cc:413
+#line 39 "parser.yy" // lalr1.cc:413
 
 # include "eval_context.h"
 
@@ -251,11 +251,10 @@ namespace isc { namespace eval {
   {
       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);
         break;
 
@@ -274,11 +273,10 @@ namespace isc { namespace eval {
     state = that.state;
       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);
         break;
 
@@ -318,18 +316,32 @@ namespace isc { namespace eval {
         << yysym.location << ": ";
     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 > (); }
-#line 326 "parser.cc" // lalr1.cc:636
+#line 331 "parser.cc" // lalr1.cc:636
         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;
 
 
@@ -441,15 +453,6 @@ namespace isc { namespace eval {
     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
        yynewstate, since the latter expects the semantical and the
        location values to have been already stored, initialize these
@@ -538,11 +541,10 @@ namespace isc { namespace eval {
          when using variants.  */
         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 > ();
         break;
 
@@ -563,43 +565,93 @@ namespace isc { namespace eval {
         {
           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;
 
   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;
 
   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;
 
   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;
 
+  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:
               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::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
   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
   EvalParser::yypgoto_[] =
   {
-      -7,    -7,    -6
+      -9,    -9,    -9,    -8,    -9,    -9
   };
 
   const signed char
   EvalParser::yydefgoto_[] =
   {
-      -1,     4,     5
+      -1,     6,     7,     8,    19,    23
   };
 
   const unsigned char
   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_[] =
   {
-       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
   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
   EvalParser::yyr1_[] =
   {
-       0,    10,    11,    11,    12,    12,    12
+       0,    16,    17,    18,    19,    19,    19,    19,    19,    20,
+      21,    21
   };
 
   const unsigned char
   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 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
   const unsigned char
   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.
@@ -968,12 +1028,12 @@ namespace isc { namespace eval {
 
 #line 21 "parser.yy" // lalr1.cc:1167
 } } // 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
-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 <eval/token.h>
-class EvalContext;
+#include <eval/eval_context_decl.h>
+#include <boost/lexical_cast.hpp>
 
 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 <cstdlib> // std::abort
@@ -125,7 +127,7 @@ using namespace isc::dhcp;
 
 #line 21 "parser.yy" // lalr1.cc:392
 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.
     union union_type
     {
-      // "option code"
-      char dummy1[sizeof(int)];
-
       // "constant string"
-      char dummy2[sizeof(std::string)];
+      // "integer"
+      // "constant hexstring"
+      // TOKEN
+      char dummy1[sizeof(std::string)];
 };
 
     /// Symbol semantic values.
@@ -321,12 +323,18 @@ namespace isc { namespace eval {
       {
         TOKEN_END = 0,
         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 int 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
     symbol_type
+    make_OPTION (const location_type& l);
+
+    static inline
+    symbol_type
     make_SUBSTRING (const location_type& l);
 
     static inline
     symbol_type
+    make_ALL (const location_type& l);
+
+    static inline
+    symbol_type
     make_COMA (const location_type& l);
 
     static inline
@@ -461,11 +475,27 @@ namespace isc { namespace eval {
 
     static inline
     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);
 
     static inline
     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.
@@ -552,7 +582,7 @@ namespace isc { namespace eval {
   // number is the opposite.  If YYTABLE_NINF, syntax error.
   static const unsigned char yytable_[];
 
-  static const unsigned char yycheck_[];
+  static const signed char yycheck_[];
 
   // YYSTOS[STATE-NUM] -- The (internal number of the) accessing
   // symbol of state STATE-NUM.
@@ -672,12 +702,12 @@ namespace isc { namespace eval {
     enum
     {
       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,
       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,     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;
 
     if (static_cast<int>(t) <= yyeof_)
@@ -755,11 +786,10 @@ namespace isc { namespace eval {
   {
       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);
         break;
 
@@ -780,11 +810,10 @@ namespace isc { namespace eval {
     (void) v;
       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);
         break;
 
@@ -804,13 +833,6 @@ namespace isc { namespace eval {
   {}
 
   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)
     : Base (t)
     , value (v)
@@ -843,11 +865,10 @@ namespace isc { namespace eval {
     // Type destructor.
     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 > ();
         break;
 
@@ -874,11 +895,10 @@ namespace isc { namespace eval {
     super_type::move(s);
       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);
         break;
 
@@ -937,7 +957,8 @@ namespace isc { namespace eval {
     const unsigned short int
     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]);
   }
@@ -955,12 +976,24 @@ namespace isc { namespace eval {
   }
 
   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)
   {
     return symbol_type (token::TOKEN_SUBSTRING, l);
   }
 
   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)
   {
     return symbol_type (token::TOKEN_COMA, l);
@@ -979,21 +1012,45 @@ namespace isc { namespace eval {
   }
 
   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)
   {
     return symbol_type (token::TOKEN_STRING, v, l);
   }
 
   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
 } } // 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 <eval/token.h>
-class EvalContext;
+#include <eval/eval_context_decl.h>
+#include <boost/lexical_cast.hpp>
 
 using namespace isc::dhcp;
+using namespace isc::eval;
 }
 // The parsing context.
 %param { EvalContext& ctx }
 %locations
-%initial-action
-{
-  // Initialize the initial location.
-  @$.begin.filename = @$.end.filename = &ctx.file;
-};
 %define parse.trace
 %define parse.error verbose
 %code
@@ -46,13 +43,21 @@ using namespace isc::dhcp;
 %token
   END  0  "end of file"
   EQUAL "=="
+  OPTION "option"
   SUBSTRING "substring"
+  ALL "all"
   COMA ","
   LPAREN  "("
   RPAREN  ")"
+  LBRACKET "["
+  RBRACKET "]"
 ;
+
 %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 << $$; } <*>;
 %%
 
@@ -60,30 +65,77 @@ using namespace isc::dhcp;
 %start 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
-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.
 
 // 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.
 
 // 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 {
 
+/// @brief Test class for testing EvalContext aka class test parsing
 class EvalContextTest : public ::testing::Test {
 public:
+    /// @brief checks if the given token is a string with the expected value
     void checkTokenString(const TokenPtr& token, const std::string& expected) {
         ASSERT_TRUE(token);
         boost::shared_ptr<TokenString> str =
@@ -45,6 +47,25 @@ public:
         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) {
         ASSERT_TRUE(token);
         boost::shared_ptr<TokenEqual> eq =
@@ -52,39 +73,115 @@ public:
         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);
         boost::shared_ptr<TokenOption> opt =
             boost::dynamic_pointer_cast<TokenOption>(token);
         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) {
 
     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) {
     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);
 
-    checkTokenString(tmp, "foo");
+    checkTokenHexString(tmp, "\a");
 }
 
+// Test the parsing of an equal expression
 TEST_F(EvalContextTest, equal) {
     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());
 
@@ -97,12 +194,117 @@ TEST_F(EvalContextTest, equal) {
     checkTokenEq(tmp3);
 }
 
+// Test the parsing of an option terminal
 TEST_F(EvalContextTest, option) {
     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);
 }
 
+// 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_length The length of the substring to get
     /// @param result_string The expected result of the eval
+    /// @param should_throw The eval will throw
     void verifySubstringEval(const std::string& test_string,
                              const std::string& test_start,
                              const std::string& test_length,
-                             const std::string& result_string) {
+                             const std::string& result_string,
+                             bool should_throw = false) {
 
         // create the token
         ASSERT_NO_THROW(t_.reset(new TokenSubstring()));
@@ -86,14 +88,19 @@ public:
         values_.push(test_length);
 
         // 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
@@ -443,13 +450,13 @@ TEST_F(TokenTest, substringStartingPosition) {
 // Check what happens if we use strings that aren't numbers for start or length
 // We should return the empty string
 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

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

@@ -121,19 +121,21 @@ TokenSubstring::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
     int length;
     try {
         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") {
             length = string_str.length();
         } else {
             length = boost::lexical_cast<int>(len_str);
         }
     } 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();

+ 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
 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.
 class EvalBadStack : public Exception {
 public:
@@ -46,6 +46,14 @@ public:
         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
 ///
 /// It provides an interface for all tokens and storage for string representation
@@ -241,6 +249,8 @@ public:
     /// - -1, -4  => "ooba"
     ///
     /// @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 values - stack of values (3 arguments will be popped, 1 result