Browse Source

[master] Finished merge of trac4231 (new boolean operators)

Francis Dupont 9 years ago
parent
commit
8e01dbe2fe

+ 6 - 0
ChangeLog

@@ -1,3 +1,9 @@
+1079.	[func]		fdupont
+	Added Not, And and Or logical operators, parentheses around
+	logical expressions and option[code].exist logical predicate
+	(to check the presence of an empty option).
+	(Trac #4231, git xxx)
+
 1078.	[func]		tomek
 1078.	[func]		tomek
 	Client classification in DHCPv4 has been enhanced. It is now
 	Client classification in DHCPv4 has been enhanced. It is now
 	possible to access relay sub-options using the expression
 	possible to access relay sub-options using the expression

+ 19 - 1
doc/guide/classify.xml

@@ -164,6 +164,7 @@
 <row><entry>Option Text</entry><entry>option[code].text</entry><entry>The value of the option with code "code" from the packet as text</entry></row>
 <row><entry>Option Text</entry><entry>option[code].text</entry><entry>The value of the option with code "code" from the packet as text</entry></row>
 -->
 -->
 <row><entry>Option Hex</entry><entry>option[code].hex</entry><entry>The value of the option with code "code" from the packet as hex</entry></row>
 <row><entry>Option Hex</entry><entry>option[code].hex</entry><entry>The value of the option with code "code" from the packet as hex</entry></row>
+<row><entry>Option Exist</entry><entry>option[code].exist</entry><entry>If the option with code "code" is present in the packet "true" else "false"</entry></row>
 <row><entry>DHCPv4 Relay Agent
 <row><entry>DHCPv4 Relay Agent
 sub-option</entry><entry>relay[code].hex</entry><entry>The value of
 sub-option</entry><entry>relay[code].hex</entry><entry>The value of
 sub-option with code "code" from the Relay Agent Information option
 sub-option with code "code" from the Relay Agent Information option
@@ -189,6 +190,11 @@ sub-option with code "code" from the Relay Agent Information option
       </para>
       </para>
 
 
       <para>
       <para>
+      "option[code].exist" checks if an option with the given code is present
+      in the incoming packet. It can be used with empty options.
+      </para>
+
+      <para>
         "relay[code].hex" attempts to extract the value of the sub-option
         "relay[code].hex" attempts to extract the value of the sub-option
         "code" from the option inserted as the Relay Agent Information
         "code" from the option inserted as the Relay Agent Information
         (82) option. If the packet doesn't contain a RAI option, or the RAI
         (82) option. If the packet doesn't contain a RAI option, or the RAI
@@ -214,6 +220,9 @@ sub-option with code "code" from the Relay Agent Information option
           </thead>
           </thead>
           <tbody>
           <tbody>
 <row><entry>Equal</entry> <entry>'foo' == 'bar'</entry><entry>Compare the two values and return "true" or "false"</entry></row>
 <row><entry>Equal</entry> <entry>'foo' == 'bar'</entry><entry>Compare the two values and return "true" or "false"</entry></row>
+<row><entry>Not</entry> <entry>not ('foo' == 'bar')</entry><entry>Logical negation</entry></row>
+<row><entry>And</entry> <entry>('foo' == 'bar') and ('bar' == 'foo')</entry><entry>Logical and</entry></row>
+<row><entry>Or</entry> <entry>('foo' == 'bar') or ('bar' == 'foo')</entry><entry>Logical or</entry></row>
 <row><entry>Substring</entry><entry>substring('foobar',0,3)</entry><entry>Return the requested substring</entry></row>
 <row><entry>Substring</entry><entry>substring('foobar',0,3)</entry><entry>Return the requested substring</entry></row>
           </tbody>
           </tbody>
           </tgroup>
           </tgroup>
@@ -221,6 +230,15 @@ sub-option with code "code" from the Relay Agent Information option
       </para>
       </para>
 
 
       <section>
       <section>
+        <title>Logical operators</title>
+        The Not, And and Or logical operators are the common operators. Not
+        has the highest precedence, Or the lowest. And and Or are (left)
+        associative, parentheses around a logical expression can be used
+        to enforce a specific grouping, for instance in "A and (B or C)"
+        (without parentheses "A and B or C" means "(A and B) or C").
+      </section>
+
+      <section>
         <title>Substring</title>
         <title>Substring</title>
         The substring operator "substring(value, start, length)" accepts both positive and
         The substring operator "substring(value, start, length)" accepts both positive and
         negative values for the starting position and the length.  For "start", a value of
         negative values for the starting position and the length.  For "start", a value of
@@ -376,7 +394,7 @@ sub-option with code "code" from the Relay Agent Information option
       </para>
       </para>
 
 
       <para>
       <para>
-     	The following example shows restricting access to a DHCPv6 subnet.  This
+        The following example shows restricting access to a DHCPv6 subnet.  This
         configuration will restrict use of the addresses 2001:db8:1::1 to
         configuration will restrict use of the addresses 2001:db8:1::1 to
         2001:db8:1::FFFF to members of the "Client_enterprise" class.
         2001:db8:1::FFFF to members of the "Client_enterprise" class.
 
 

+ 2 - 9
src/lib/eval/evaluate.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -19,14 +19,7 @@ bool evaluate(const Expression& expr, const Pkt& pkt) {
         isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly "
         isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly "
                   "1 value at the end of evaluatuion, got " << values.size());
                   "1 value at the end of evaluatuion, got " << values.size());
     }
     }
-    if (values.top() == "false") {
+    return (Token::toBool(values.top()));
-        return (false);
-    } else if (values.top() == "true") {
-        return (true);
-    } else {
-        isc_throw(EvalTypeError, "Incorrect evaluation type. Expected "
-                  "\"false\" or \"true\", got \"" << values.top() << "\"");
-    }
 }
 }
 
 
 }; // end of isc::dhcp namespace
 }; // end of isc::dhcp namespace

+ 184 - 155
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 39
+#define YY_FLEX_SUBMINOR_VERSION 35
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #define FLEX_BETA
 #define FLEX_BETA
 #endif
 #endif
@@ -72,6 +72,7 @@ typedef int16_t flex_int16_t;
 typedef uint16_t flex_uint16_t;
 typedef uint16_t flex_uint16_t;
 typedef int32_t flex_int32_t;
 typedef int32_t flex_int32_t;
 typedef uint32_t flex_uint32_t;
 typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
 #else
 #else
 typedef signed char flex_int8_t;
 typedef signed char flex_int8_t;
 typedef short int flex_int16_t;
 typedef short int flex_int16_t;
@@ -79,6 +80,7 @@ typedef int flex_int32_t;
 typedef unsigned char flex_uint8_t; 
 typedef unsigned char flex_uint8_t; 
 typedef unsigned short int flex_uint16_t;
 typedef unsigned short int flex_uint16_t;
 typedef unsigned int flex_uint32_t;
 typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
 
 
 /* Limits of integral types. */
 /* Limits of integral types. */
 #ifndef INT8_MIN
 #ifndef INT8_MIN
@@ -109,8 +111,6 @@ typedef unsigned int flex_uint32_t;
 #define UINT32_MAX             (4294967295U)
 #define UINT32_MAX             (4294967295U)
 #endif
 #endif
 
 
-#endif /* ! C99 */
-
 #endif /* ! FLEXINT_H */
 #endif /* ! FLEXINT_H */
 
 
 /* %endif */
 /* %endif */
@@ -185,15 +185,7 @@ typedef unsigned int flex_uint32_t;
 
 
 /* Size of default input buffer. */
 /* Size of default input buffer. */
 #ifndef YY_BUF_SIZE
 #ifndef YY_BUF_SIZE
-#ifdef __ia64__
-/* On IA-64, the buffer size is 16k, not 8k.
- * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
- * Ditto for the __ia64__ case accordingly.
- */
-#define YY_BUF_SIZE 32768
-#else
 #define YY_BUF_SIZE 16384
 #define YY_BUF_SIZE 16384
-#endif /* __ia64__ */
 #endif
 #endif
 
 
 /* The state buf must be large enough to hold one state per character in the main buffer.
 /* The state buf must be large enough to hold one state per character in the main buffer.
@@ -233,18 +225,11 @@ extern FILE *yyin, *yyout;
      */
      */
     #define  YY_LESS_LINENO(n) \
     #define  YY_LESS_LINENO(n) \
             do { \
             do { \
-                int yyl;\
+                yy_size_t yyl;\
                 for ( yyl = n; yyl < yyleng; ++yyl )\
                 for ( yyl = n; yyl < yyleng; ++yyl )\
                     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) \
@@ -435,7 +420,7 @@ void yyfree (void *  );
 /* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */
 /* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */
 /* Begin user sect3 */
 /* Begin user sect3 */
 
 
-#define yywrap() 1
+#define yywrap(n) 1
 #define YY_SKIP_YYWRAP
 #define YY_SKIP_YYWRAP
 
 
 #define FLEX_DEBUG
 #define FLEX_DEBUG
@@ -453,8 +438,6 @@ 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 );
@@ -470,15 +453,15 @@ static void yy_fatal_error (yyconst char msg[]  );
 #define YY_DO_BEFORE_ACTION \
 #define YY_DO_BEFORE_ACTION \
 	(yytext_ptr) = yy_bp; \
 	(yytext_ptr) = yy_bp; \
 /* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\
 /* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\
-	yyleng = (size_t) (yy_cp - yy_bp); \
+	yyleng = (yy_size_t) (yy_cp - yy_bp); \
 	(yy_hold_char) = *yy_cp; \
 	(yy_hold_char) = *yy_cp; \
 	*yy_cp = '\0'; \
 	*yy_cp = '\0'; \
 /* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\
 /* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\
 	(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 21
+#define YY_NUM_RULES 25
-#define YY_END_OF_BUFFER 22
+#define YY_END_OF_BUFFER 26
 /* 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
@@ -486,30 +469,33 @@ 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_acclist[99] =
+static yyconst flex_int16_t yy_acclist[119] =
     {   0,
     {   0,
-       22,   20,   21,    1,   20,   21,    2,   21,   20,   21,
+       26,   24,   25,    1,   24,   25,    2,   25,   24,   25,
-       15,   20,   21,   16,   20,   21,   19,   20,   21,   20,
+       19,   24,   25,   20,   24,   25,   23,   24,   25,   24,
-       21,   14,   20,   21,    5,   20,   21,    5,   20,   21,
+       25,   18,   24,   25,    5,   24,   25,    5,   24,   25,
-       20,   21,   20,   21,16390,   17,   20,   21,   18,   20,
+       24,   25,   24,   25,16390,   21,   24,   25,   22,   24,
-       21,   20,   21,16390,   20,   21,16390,   20,   21,16390,
+       25,   24,   25,16390,   24,   25,16390,   24,   25,16390,
-       20,   21,16390,   20,   21,16390,   20,   21,16390,    1,
+       24,   25,16390,   24,   25,16390,   24,   25,16390,   24,
-        2,    3,    5,    7,16390, 8198,16390,16390,16390,16390,
+       25,16390,   24,   25,16390,    1,    2,    3,    5,    7,
-    16390,16390,    4,   13,16390,   10,16390,16390,16390,16390,
+    16390, 8198,16390,16390,16390,16390,16390,16390,   17,16390,
-    16390,16390,16390,16390,    9,16390,16390,16390,16390,    8,
+    16390,16390,16390,    4,   14,16390,   16,16390,16390,   10,
-    16390,   12,16390,16390,16390,16390,   11,16390
+    16390,   15,16390,16390,16390,16390,16390,16390,16390,16390,
-
+
+    16390,    9,16390,16390,16390,16390,16390,   11,16390,    8,
+    16390,   13,16390,16390,16390,16390,   12,16390
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_accept[64] =
+static yyconst flex_int16_t yy_accept[76] =
     {   0,
     {   0,
         1,    1,    1,    2,    4,    7,    9,   11,   14,   17,
         1,    1,    1,    2,    4,    7,    9,   11,   14,   17,
        20,   22,   25,   28,   31,   33,   36,   39,   42,   45,
        20,   22,   25,   28,   31,   33,   36,   39,   42,   45,
-       48,   51,   54,   57,   60,   61,   62,   62,   63,   64,
+       48,   51,   54,   57,   60,   63,   66,   67,   68,   68,
-       64,   65,   65,   65,   66,   67,   68,   69,   70,   71,
+       69,   70,   70,   71,   71,   71,   72,   73,   74,   75,
-       72,   73,   74,   76,   78,   79,   80,   81,   82,   83,
+       76,   77,   78,   79,   81,   82,   83,   84,   85,   87,
-       84,   85,   87,   88,   89,   90,   92,   94,   95,   96,
+       89,   90,   92,   94,   95,   96,   97,   98,   99,  100,
-       97,   99,   99
+      101,  102,  104,  105,  106,  107,  108,  110,  112,  114,
+      115,  116,  117,  119,  119
     } ;
     } ;
 
 
 static yyconst flex_int32_t yy_ec[256] =
 static yyconst flex_int32_t yy_ec[256] =
@@ -523,11 +509,11 @@ static yyconst flex_int32_t yy_ec[256] =
        13,    1,    1,    1,   14,   14,   14,   14,   14,   14,
        13,    1,    1,    1,   14,   14,   14,   14,   14,   14,
        15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
        15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
        15,   15,   15,   15,   15,   15,   15,   16,   15,   15,
        15,   15,   15,   15,   15,   15,   15,   16,   15,   15,
-       17,    1,   18,    1,   19,    1,   20,   21,   14,   14,
+       17,    1,   18,    1,   19,    1,   20,   21,   14,   22,
 
 
-       22,   14,   23,   24,   25,   15,   15,   26,   15,   27,
+       23,   14,   24,   25,   26,   15,   15,   27,   15,   28,
-       28,   29,   15,   30,   31,   32,   33,   15,   15,   34,
+       29,   30,   15,   31,   32,   33,   34,   15,   15,   35,
-       35,   15,    1,    1,    1,    1,    1,    1,    1,    1,
+       36,   15,    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,
@@ -544,95 +530,114 @@ 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[36] =
+static yyconst flex_int32_t yy_meta[37] =
     {   0,
     {   0,
         1,    2,    3,    1,    1,    1,    1,    2,    1,    4,
         1,    2,    3,    1,    1,    1,    1,    2,    1,    4,
         4,    4,    1,    4,    2,    2,    1,    2,    2,    4,
         4,    4,    1,    4,    2,    2,    1,    2,    2,    4,
-        4,    4,    2,    2,    2,    2,    2,    2,    2,    2,
+        4,    4,    4,    2,    2,    2,    2,    2,    2,    2,
-        2,    2,    2,    2,    2
+        2,    2,    2,    2,    2,    2
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_base[66] =
+static yyconst flex_int16_t yy_base[78] =
     {   0,
     {   0,
-        0,    0,  135,  136,  132,  130,  128,  136,  136,  136,
+        0,    0,  113,  205,  108,  101,   98,  205,  205,  205,
-       26,  136,   29,   32,  118,   46,  136,  136,   64,   24,
+       27,  205,   30,   33,   87,   45,  205,  205,   64,   22,
-       26,   25,   27,   35,  128,  126,  124,  136,   58,    0,
+       28,   31,   43,   52,   34,   58,   82,   66,   50,  205,
-      136,   56,   73,  101,  136,  100,   45,   30,   99,   52,
+       66,    0,  205,   85,   87,   66,  205,   68,   79,   71,
-       51,    0,   98,   97,   55,   66,   58,   61,   68,   62,
+       81,   84,   91,   93,   95,  104,   99,    0,  101,  108,
-       69,   96,   76,   86,   74,   95,   90,   80,   81,   87,
+      110,  112,  116,  119,  121,  123,  125,  129,  132,  136,
-       85,  136,  113,  116,  105
+      138,  140,  142,  148,  161,  150,  152,  155,  157,  164,
+      159,  169,  167,  205,  197,  200,   48
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_def[66] =
+static yyconst flex_int16_t yy_def[78] =
     {   0,
     {   0,
-       62,    1,   62,   62,   62,   62,   63,   62,   62,   62,
+       74,    1,   74,   74,   74,   74,   75,   74,   74,   74,
-       62,   62,   62,   62,   62,   64,   62,   62,   64,   19,
+       74,   74,   74,   74,   74,   76,   74,   74,   76,   19,
-       19,   19,   19,   19,   62,   62,   63,   62,   62,   65,
+       19,   19,   19,   19,   19,   19,   74,   74,   75,   74,
-       62,   62,   19,   19,   62,   19,   19,   19,   19,   19,
+       74,   77,   74,   74,   19,   19,   74,   19,   19,   19,
-       19,   65,   19,   19,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19,   77,   19,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
        19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
        19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
-       19,    0,   62,   62,   62
+       19,   19,   19,    0,   74,   74,   74
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_nxt[172] =
+static yyconst flex_int16_t yy_nxt[242] =
     {   0,
     {   0,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
        14,   14,   15,   16,   16,   16,   17,   18,    4,   19,
        14,   14,   15,   16,   16,   16,   17,   18,    4,   19,
-       16,   16,   16,   20,   16,   16,   16,   21,   16,   22,
+       16,   16,   20,   16,   21,   16,   16,   22,   23,   16,
-       23,   24,   16,   16,   16,   29,   29,   29,   29,   29,
+       24,   25,   26,   16,   16,   16,   31,   31,   31,   31,
-       29,   29,   29,   29,   30,   37,   39,   32,   32,   34,
+       31,   31,   31,   31,   31,   32,   34,   34,   36,   36,
-       34,   34,   34,   33,   38,   34,   41,   32,   32,   40,
+       41,   48,   35,   30,   36,   36,   40,   36,   36,   42,
-       34,   45,   30,   35,   33,   32,   32,   29,   29,   29,
+       36,   36,   37,   35,   32,   34,   34,   46,   28,   36,
-       34,   33,   47,   35,   62,   62,   34,   34,   44,   49,
+       36,   35,   43,   44,   45,   31,   31,   31,   36,   36,
-       34,   35,   33,   34,   48,   50,   34,   34,   51,   36,
+       47,   37,   35,   27,   36,   36,   34,   34,   74,   74,
-       62,   34,   52,   34,   34,   53,   54,   57,   34,   34,
+       38,   39,   36,   36,   49,   36,   51,   36,   36,   33,
-
+
-       55,   34,   56,   58,   59,   34,   34,   60,   42,   61,
+       50,   30,   37,   28,   74,   36,   36,   36,   36,   27,
-       34,   34,   34,   27,   27,   34,   27,   34,   34,   34,
+       36,   36,   74,   36,   36,   52,   53,   36,   36,   36,
-       34,   34,   34,   34,   46,   43,   34,   28,   26,   25,
+       36,   55,   36,   54,   56,   36,   36,   36,   36,   74,
-       31,   28,   26,   25,   62,    3,   62,   62,   62,   62,
+       36,   36,   74,   57,   36,   36,   36,   36,   36,   36,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
+       60,   58,   36,   36,   59,   36,   36,   36,   36,   36,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
+       36,   36,   36,   74,   61,   36,   36,   62,   36,   36,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
+       64,   63,   36,   36,   36,   36,   36,   36,   36,   36,
-       62
+       66,   65,   69,   67,   36,   68,   36,   36,   36,   36,
+       70,   36,   36,   36,   36,   36,   72,   36,   36,   71,
+       36,   36,   73,   36,   36,   36,   36,   29,   29,   74,
+
+       29,   36,   36,   36,    3,   74,   74,   74,   74,   74,
+       74,   74,   74,   74,   74,   74,   74,   74,   74,   74,
+       74,   74,   74,   74,   74,   74,   74,   74,   74,   74,
+       74,   74,   74,   74,   74,   74,   74,   74,   74,   74,
+       74
     } ;
     } ;
 
 
-static yyconst flex_int16_t yy_chk[172] =
+static yyconst flex_int16_t yy_chk[242] =
     {   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,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    1,    1,    1,   11,   11,   11,   13,   13,
+        1,    1,    1,    1,    1,    1,   11,   11,   11,   13,
-       13,   14,   14,   14,   13,   20,   22,   16,   16,   20,
+       13,   13,   14,   14,   14,   13,   16,   16,   20,   20,
-       22,   21,   23,   16,   21,   38,   24,   32,   32,   23,
+       21,   77,   16,   29,   21,   21,   20,   22,   22,   22,
-       24,   38,   13,   16,   16,   19,   19,   29,   29,   29,
+       25,   25,   16,   16,   13,   19,   19,   25,   28,   23,
-       37,   19,   40,   32,   33,   33,   41,   40,   37,   45,
+       23,   19,   23,   23,   24,   31,   31,   31,   24,   24,
-       45,   19,   19,   47,   41,   46,   48,   50,   47,   19,
+       26,   19,   19,   27,   26,   26,   34,   34,   35,   35,
-       33,   46,   48,   49,   51,   49,   50,   54,   33,   55,
+       19,   19,   36,   36,   38,   38,   40,   40,   40,   15,
-
+
-       51,   53,   53,   55,   58,   58,   59,   59,   65,   60,
+       39,    7,   34,    6,   35,   39,   39,   41,   41,    5,
-       61,   54,   60,   63,   63,   57,   63,   64,   64,   64,
+       42,   42,    3,   35,   35,   41,   42,   43,   43,   44,
-       56,   52,   44,   43,   39,   36,   34,   27,   26,   25,
+       44,   45,   45,   43,   46,   47,   47,   49,   49,    0,
-       15,    7,    6,    5,    3,   62,   62,   62,   62,   62,
+       46,   46,    0,   47,   50,   50,   51,   51,   52,   52,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
+       55,   51,   53,   53,   54,   54,   54,   55,   55,   56,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
+       56,   57,   57,    0,   56,   58,   58,   57,   59,   59,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
+       59,   58,   60,   60,   61,   61,   62,   62,   63,   63,
-       62
+       61,   60,   65,   63,   64,   64,   66,   66,   67,   67,
+       66,   68,   68,   69,   69,   71,   71,   65,   65,   70,
+       70,   70,   72,   73,   73,   72,   72,   75,   75,    0,
+
+       75,   76,   76,   76,   74,   74,   74,   74,   74,   74,
+       74,   74,   74,   74,   74,   74,   74,   74,   74,   74,
+       74,   74,   74,   74,   74,   74,   74,   74,   74,   74,
+       74,   74,   74,   74,   74,   74,   74,   74,   74,   74,
+       74
     } ;
     } ;
 
 
 /* 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[22] =
+static yyconst flex_int32_t yy_rule_can_match_eol[26] =
     {   0,
     {   0,
 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-    0, 0,     };
+    0, 0, 0, 0, 0, 0,     };
 
 
 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[21] =
+static yyconst flex_int16_t yy_rule_linenum[25] =
     {   0,
     {   0,
        78,   82,   88,   98,  104,  118,  125,  126,  127,  128,
        78,   82,   88,   98,  104,  118,  125,  126,  127,  128,
-      129,  130,  131,  132,  133,  134,  135,  136,  137,  139
+      129,  130,  131,  132,  133,  134,  135,  136,  137,  138,
+      139,  140,  141,  143
     } ;
     } ;
 
 
 static yy_state_type *yy_state_buf=0, *yy_state_ptr=0;
 static yy_state_type *yy_state_buf=0, *yy_state_ptr=0;
@@ -709,7 +714,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 713 "lexer.cc"
+#line 718 "lexer.cc"
 
 
 #define INITIAL 0
 #define INITIAL 0
 
 
@@ -818,12 +823,7 @@ static int input (void );
 
 
 /* Amount of stuff to slurp up with each read. */
 /* Amount of stuff to slurp up with each read. */
 #ifndef YY_READ_BUF_SIZE
 #ifndef YY_READ_BUF_SIZE
-#ifdef __ia64__
-/* On IA-64, the buffer size is 16k, not 8k */
-#define YY_READ_BUF_SIZE 16384
-#else
 #define YY_READ_BUF_SIZE 8192
 #define YY_READ_BUF_SIZE 8192
-#endif /* __ia64__ */
 #endif
 #endif
 
 
 /* Copy whatever the last rule matched to the standard output. */
 /* Copy whatever the last rule matched to the standard output. */
@@ -832,7 +832,7 @@ static int input (void );
 /* This used to be an fputs(), but since the string might contain NUL's,
 /* This used to be an fputs(), but since the string might contain NUL's,
  * we now use fwrite().
  * we now use fwrite().
  */
  */
-#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
 /* %endif */
 /* %endif */
 /* %if-c++-only C++ definition */
 /* %if-c++-only C++ definition */
 /* %endif */
 /* %endif */
@@ -847,7 +847,7 @@ static int input (void );
 	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
 	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
 		{ \
 		{ \
 		int c = '*'; \
 		int c = '*'; \
-		size_t n; \
+		yy_size_t n; \
 		for ( n = 0; n < max_size && \
 		for ( n = 0; n < max_size && \
 			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
 			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
 			buf[n] = (char) c; \
 			buf[n] = (char) c; \
@@ -953,6 +953,17 @@ 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 71 "lexer.ll"
+
+
+
+    // Code run each time yylex is called.
+    loc.step();
+
+
+#line 966 "lexer.cc"
+
 	if ( !(yy_init) )
 	if ( !(yy_init) )
 		{
 		{
 		(yy_init) = 1;
 		(yy_init) = 1;
@@ -993,18 +1004,6 @@ YY_DECL
 		yy_load_buffer_state( );
 		yy_load_buffer_state( );
 		}
 		}
 
 
-	{
-/* %% [7.0] user's declarations go here */
-#line 71 "lexer.ll"
-
-
-
-    // Code run each time yylex is called.
-    loc.step();
-
-
-#line 1007 "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 */
@@ -1027,23 +1026,24 @@ 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)];
 			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 >= 63 )
+				if ( yy_current_state >= 75 )
 					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_state_ptr)++ = yy_current_state;
 			*(yy_state_ptr)++ = yy_current_state;
 			++yy_cp;
 			++yy_cp;
 			}
 			}
-		while ( yy_current_state != 62 );
+		while ( yy_current_state != 74 );
 
 
 yy_find_action:
 yy_find_action:
 /* %% [10.0] code to find the action number goes here */
 /* %% [10.0] code to find the action number goes here */
 		yy_current_state = *--(yy_state_ptr);
 		yy_current_state = *--(yy_state_ptr);
 		(yy_lp) = yy_accept[yy_current_state];
 		(yy_lp) = yy_accept[yy_current_state];
+goto find_rule; /* Shut up GCC warning -Wall */
 find_rule: /* we branch to this label when backing up */
 find_rule: /* we branch to this label when backing up */
 		for ( ; ; ) /* until we find what rule we matched */
 		for ( ; ; ) /* until we find what rule we matched */
 			{
 			{
@@ -1101,13 +1101,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 < 21 )
+			else if ( yy_act < 25 )
 				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 == 21 )
+			else if ( yy_act == 25 )
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				         yytext );
 				         yytext );
-			else if ( yy_act == 22 )
+			else if ( yy_act == 26 )
 				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 );
@@ -1207,63 +1207,83 @@ return isc::eval::EvalParser::make_HEX(loc);
 case 11:
 case 11:
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 129 "lexer.ll"
 #line 129 "lexer.ll"
-return isc::eval::EvalParser::make_SUBSTRING(loc);
+return isc::eval::EvalParser::make_EXISTS(loc);
 	YY_BREAK
 	YY_BREAK
 case 12:
 case 12:
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 130 "lexer.ll"
 #line 130 "lexer.ll"
-return isc::eval::EvalParser::make_RELAY4(loc);
+return isc::eval::EvalParser::make_SUBSTRING(loc);
 	YY_BREAK
 	YY_BREAK
 case 13:
 case 13:
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 131 "lexer.ll"
 #line 131 "lexer.ll"
-return isc::eval::EvalParser::make_ALL(loc);
+return isc::eval::EvalParser::make_RELAY4(loc);
 	YY_BREAK
 	YY_BREAK
 case 14:
 case 14:
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 132 "lexer.ll"
 #line 132 "lexer.ll"
-return isc::eval::EvalParser::make_DOT(loc);
+return isc::eval::EvalParser::make_ALL(loc);
 	YY_BREAK
 	YY_BREAK
 case 15:
 case 15:
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 133 "lexer.ll"
 #line 133 "lexer.ll"
-return isc::eval::EvalParser::make_LPAREN(loc);
+return isc::eval::EvalParser::make_NOT(loc);
 	YY_BREAK
 	YY_BREAK
 case 16:
 case 16:
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 134 "lexer.ll"
 #line 134 "lexer.ll"
-return isc::eval::EvalParser::make_RPAREN(loc);
+return isc::eval::EvalParser::make_AND(loc);
 	YY_BREAK
 	YY_BREAK
 case 17:
 case 17:
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 135 "lexer.ll"
 #line 135 "lexer.ll"
-return isc::eval::EvalParser::make_LBRACKET(loc);
+return isc::eval::EvalParser::make_OR(loc);
 	YY_BREAK
 	YY_BREAK
 case 18:
 case 18:
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 136 "lexer.ll"
 #line 136 "lexer.ll"
-return isc::eval::EvalParser::make_RBRACKET(loc);
+return isc::eval::EvalParser::make_DOT(loc);
 	YY_BREAK
 	YY_BREAK
 case 19:
 case 19:
 YY_RULE_SETUP
 YY_RULE_SETUP
 #line 137 "lexer.ll"
 #line 137 "lexer.ll"
-return isc::eval::EvalParser::make_COMA(loc);
+return isc::eval::EvalParser::make_LPAREN(loc);
 	YY_BREAK
 	YY_BREAK
 case 20:
 case 20:
 YY_RULE_SETUP
 YY_RULE_SETUP
+#line 138 "lexer.ll"
+return isc::eval::EvalParser::make_RPAREN(loc);
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
 #line 139 "lexer.ll"
 #line 139 "lexer.ll"
+return isc::eval::EvalParser::make_LBRACKET(loc);
+	YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 140 "lexer.ll"
+return isc::eval::EvalParser::make_RBRACKET(loc);
+	YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 141 "lexer.ll"
+return isc::eval::EvalParser::make_COMA(loc);
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 143 "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 140 "lexer.ll"
+#line 144 "lexer.ll"
 return isc::eval::EvalParser::make_END(loc);
 return isc::eval::EvalParser::make_END(loc);
 	YY_BREAK
 	YY_BREAK
-case 21:
+case 25:
 YY_RULE_SETUP
 YY_RULE_SETUP
-#line 141 "lexer.ll"
+#line 145 "lexer.ll"
 ECHO;
 ECHO;
 	YY_BREAK
 	YY_BREAK
-#line 1267 "lexer.cc"
+#line 1287 "lexer.cc"
 
 
 	case YY_END_OF_BUFFER:
 	case YY_END_OF_BUFFER:
 		{
 		{
@@ -1393,7 +1413,6 @@ 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 */
 
 
@@ -1544,7 +1563,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 >= 63 )
+			if ( yy_current_state >= 75 )
 				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];
@@ -1572,15 +1591,15 @@ 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 >= 63 )
+		if ( yy_current_state >= 75 )
 			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 == 62);
+	yy_is_jam = (yy_current_state == 74);
 	if ( ! yy_is_jam )
 	if ( ! yy_is_jam )
 		*(yy_state_ptr)++ = yy_current_state;
 		*(yy_state_ptr)++ = yy_current_state;
 
 
-		return yy_is_jam ? 0 : yy_current_state;
+	return yy_is_jam ? 0 : yy_current_state;
 }
 }
 
 
 /* %if-c-only */
 /* %if-c-only */
@@ -1639,7 +1658,7 @@ static int yy_get_next_buffer (void)
 				case EOB_ACT_END_OF_FILE:
 				case EOB_ACT_END_OF_FILE:
 					{
 					{
 					if ( yywrap( ) )
 					if ( yywrap( ) )
-						return EOF;
+						return 0;
 
 
 					if ( ! (yy_did_buffer_switch_on_eof) )
 					if ( ! (yy_did_buffer_switch_on_eof) )
 						YY_NEW_FILE;
 						YY_NEW_FILE;
@@ -1803,6 +1822,17 @@ static void yy_load_buffer_state  (void)
 	yyfree((void *) b  );
 	yyfree((void *) b  );
 }
 }
 
 
+/* %if-c-only */
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* %endif */
+
+/* %if-c++-only */
+/* %endif */
+
 /* Initializes or reinitializes a buffer.
 /* Initializes or reinitializes a buffer.
  * This function is sometimes called more than once on the same buffer,
  * This function is sometimes called more than once on the same buffer,
  * such as during a yyrestart() or at EOF.
  * such as during a yyrestart() or at EOF.
@@ -2043,8 +2073,8 @@ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
 /* %if-c-only */
 /* %if-c-only */
 /** Setup the input buffer state to scan the given bytes. The next call to yylex() will
 /** Setup the input buffer state to scan the given bytes. The next call to yylex() will
  * scan from a @e copy of @a bytes.
  * scan from a @e copy of @a bytes.
- * @param yybytes the byte buffer to scan
+ * @param bytes the byte buffer to scan
- * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
  * 
  * 
  * @return the newly allocated buffer state object.
  * @return the newly allocated buffer state object.
  */
  */
@@ -2052,8 +2082,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, 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;
@@ -2324,7 +2353,7 @@ void yyfree (void * ptr )
 
 
 /* %ok-for-header */
 /* %ok-for-header */
 
 
-#line 140 "lexer.ll"
+#line 145 "lexer.ll"
 
 
 
 
 
 
@@ -2339,7 +2368,7 @@ EvalContext::scanStringBegin()
     buffer = yy_scan_bytes(string_.c_str(),string_.size());
     buffer = yy_scan_bytes(string_.c_str(),string_.size());
     if (!buffer) {
     if (!buffer) {
         fatal("cannot scan string");
         fatal("cannot scan string");
-	// fatal() throws an exception so this can't be reached
+        // fatal() throws an exception so this can't be reached
     }
     }
 }
 }
 
 

+ 5 - 1
src/lib/eval/lexer.ll

@@ -126,9 +126,13 @@ blank [ \t]
 "option"    return isc::eval::EvalParser::make_OPTION(loc);
 "option"    return isc::eval::EvalParser::make_OPTION(loc);
 "text"      return isc::eval::EvalParser::make_TEXT(loc);
 "text"      return isc::eval::EvalParser::make_TEXT(loc);
 "hex"       return isc::eval::EvalParser::make_HEX(loc);
 "hex"       return isc::eval::EvalParser::make_HEX(loc);
+"exists"    return isc::eval::EvalParser::make_EXISTS(loc);
 "substring" return isc::eval::EvalParser::make_SUBSTRING(loc);
 "substring" return isc::eval::EvalParser::make_SUBSTRING(loc);
 "relay4"    return isc::eval::EvalParser::make_RELAY4(loc);
 "relay4"    return isc::eval::EvalParser::make_RELAY4(loc);
 "all"       return isc::eval::EvalParser::make_ALL(loc);
 "all"       return isc::eval::EvalParser::make_ALL(loc);
+"not"       return isc::eval::EvalParser::make_NOT(loc);
+"and"       return isc::eval::EvalParser::make_AND(loc);
+"or"        return isc::eval::EvalParser::make_OR(loc);
 "."         return isc::eval::EvalParser::make_DOT(loc);
 "."         return isc::eval::EvalParser::make_DOT(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);
@@ -151,7 +155,7 @@ EvalContext::scanStringBegin()
     buffer = yy_scan_bytes(string_.c_str(), string_.size());
     buffer = yy_scan_bytes(string_.c_str(), string_.size());
     if (!buffer) {
     if (!buffer) {
         fatal("cannot scan string");
         fatal("cannot scan string");
-	// fatal() throws an exception so this can't be reached
+        // fatal() throws an exception so this can't be reached
     }
     }
 }
 }
 
 

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

@@ -1,4 +1,5 @@
-// A Bison parser, made by GNU Bison 3.0.2.
+// Generated 20160219
+// A Bison parser, made by GNU Bison 3.0.4.
 
 
 // Locations for Bison parsers in C++
 // Locations for Bison parsers in C++
 
 

+ 206 - 149
src/lib/eval/parser.cc

@@ -1,8 +1,8 @@
-// A Bison parser, made by GNU Bison 3.0.2.
+// A Bison parser, made by GNU Bison 3.0.4.
 
 
 // Skeleton implementation for Bison LALR(1) parsers in C++
 // Skeleton implementation for Bison LALR(1) parsers in C++
 
 
-// Copyright (C) 2002-2013 Free Software Foundation, Inc.
+// Copyright (C) 2002-2015 Free Software Foundation, Inc.
 
 
 // This program is free software: you can redistribute it and/or modify
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
 // it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@
 
 
 // First part of user declarations.
 // First part of user declarations.
 
 
-#line 37 "parser.cc" // lalr1.cc:399
+#line 37 "parser.cc" // lalr1.cc:404
 
 
 # ifndef YY_NULLPTR
 # ifndef YY_NULLPTR
 #  if defined __cplusplus && 201103L <= __cplusplus
 #  if defined __cplusplus && 201103L <= __cplusplus
@@ -47,13 +47,13 @@
 
 
 // User implementation prologue.
 // User implementation prologue.
 
 
-#line 51 "parser.cc" // lalr1.cc:407
+#line 51 "parser.cc" // lalr1.cc:412
 // Unqualified %code blocks.
 // Unqualified %code blocks.
-#line 32 "parser.yy" // lalr1.cc:408
+#line 32 "parser.yy" // lalr1.cc:413
 
 
 # include "eval_context.h"
 # include "eval_context.h"
 
 
-#line 57 "parser.cc" // lalr1.cc:408
+#line 57 "parser.cc" // lalr1.cc:413
 
 
 
 
 #ifndef YY_
 #ifndef YY_
@@ -130,16 +130,16 @@
 #endif // !YYDEBUG
 #endif // !YYDEBUG
 
 
 #define yyerrok         (yyerrstatus_ = 0)
 #define yyerrok         (yyerrstatus_ = 0)
-#define yyclearin       (yyempty = true)
+#define yyclearin       (yyla.clear ())
 
 
 #define YYACCEPT        goto yyacceptlab
 #define YYACCEPT        goto yyacceptlab
 #define YYABORT         goto yyabortlab
 #define YYABORT         goto yyabortlab
 #define YYERROR         goto yyerrorlab
 #define YYERROR         goto yyerrorlab
 #define YYRECOVERING()  (!!yyerrstatus_)
 #define YYRECOVERING()  (!!yyerrstatus_)
 
 
-#line 13 "parser.yy" // lalr1.cc:474
+#line 13 "parser.yy" // lalr1.cc:479
 namespace isc { namespace eval {
 namespace isc { namespace eval {
-#line 143 "parser.cc" // lalr1.cc:474
+#line 143 "parser.cc" // lalr1.cc:479
 
 
   /* Return YYSTR after stripping away unnecessary quotes and
   /* Return YYSTR after stripping away unnecessary quotes and
      backslashes, so that it's suitable for yyerror.  The heuristic is
      backslashes, so that it's suitable for yyerror.  The heuristic is
@@ -202,7 +202,7 @@ namespace isc { namespace eval {
   // by_state.
   // by_state.
   inline
   inline
   EvalParser::by_state::by_state ()
   EvalParser::by_state::by_state ()
-    : state (empty)
+    : state (empty_state)
   {}
   {}
 
 
   inline
   inline
@@ -212,10 +212,17 @@ namespace isc { namespace eval {
 
 
   inline
   inline
   void
   void
+  EvalParser::by_state::clear ()
+  {
+    state = empty_state;
+  }
+
+  inline
+  void
   EvalParser::by_state::move (by_state& that)
   EvalParser::by_state::move (by_state& that)
   {
   {
     state = that.state;
     state = that.state;
-    that.state = empty;
+    that.clear ();
   }
   }
 
 
   inline
   inline
@@ -227,7 +234,10 @@ namespace isc { namespace eval {
   EvalParser::symbol_number_type
   EvalParser::symbol_number_type
   EvalParser::by_state::type_get () const
   EvalParser::by_state::type_get () const
   {
   {
-    return state == empty ? 0 : yystos_[state];
+    if (state == empty_state)
+      return empty_symbol;
+    else
+      return yystos_[state];
   }
   }
 
 
   inline
   inline
@@ -241,19 +251,19 @@ namespace isc { namespace eval {
   {
   {
       switch (that.type_get ())
       switch (that.type_get ())
     {
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.move< TokenOption::RepresentationType > (that.value);
         value.move< TokenOption::RepresentationType > (that.value);
         break;
         break;
 
 
-      case 16: // "constant string"
+      case 20: // "constant string"
-      case 17: // "integer"
+      case 21: // "integer"
-      case 18: // "constant hexstring"
+      case 22: // "constant hexstring"
-      case 19: // "option name"
+      case 23: // "option name"
-      case 20: // TOKEN
+      case 24: // TOKEN
         value.move< std::string > (that.value);
         value.move< std::string > (that.value);
         break;
         break;
 
 
-      case 25: // option_code
+      case 29: // option_code
         value.move< uint16_t > (that.value);
         value.move< uint16_t > (that.value);
         break;
         break;
 
 
@@ -262,7 +272,7 @@ namespace isc { namespace eval {
     }
     }
 
 
     // that is emptied.
     // that is emptied.
-    that.type = empty;
+    that.type = empty_symbol;
   }
   }
 
 
   inline
   inline
@@ -272,19 +282,19 @@ namespace isc { namespace eval {
     state = that.state;
     state = that.state;
       switch (that.type_get ())
       switch (that.type_get ())
     {
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.copy< TokenOption::RepresentationType > (that.value);
         value.copy< TokenOption::RepresentationType > (that.value);
         break;
         break;
 
 
-      case 16: // "constant string"
+      case 20: // "constant string"
-      case 17: // "integer"
+      case 21: // "integer"
-      case 18: // "constant hexstring"
+      case 22: // "constant hexstring"
-      case 19: // "option name"
+      case 23: // "option name"
-      case 20: // TOKEN
+      case 24: // TOKEN
         value.copy< std::string > (that.value);
         value.copy< std::string > (that.value);
         break;
         break;
 
 
-      case 25: // option_code
+      case 29: // option_code
         value.copy< uint16_t > (that.value);
         value.copy< uint16_t > (that.value);
         break;
         break;
 
 
@@ -315,58 +325,62 @@ namespace isc { namespace eval {
     std::ostream& yyoutput = yyo;
     std::ostream& yyoutput = yyo;
     YYUSE (yyoutput);
     YYUSE (yyoutput);
     symbol_number_type yytype = yysym.type_get ();
     symbol_number_type yytype = yysym.type_get ();
+    // Avoid a (spurious) G++ 4.8 warning about "array subscript is
+    // below array bounds".
+    if (yysym.empty ())
+      std::abort ();
     yyo << (yytype < yyntokens_ ? "token" : "nterm")
     yyo << (yytype < yyntokens_ ? "token" : "nterm")
         << ' ' << yytname_[yytype] << " ("
         << ' ' << yytname_[yytype] << " ("
         << yysym.location << ": ";
         << yysym.location << ": ";
     switch (yytype)
     switch (yytype)
     {
     {
-            case 16: // "constant string"
+            case 20: // "constant string"
 
 
-#line 63 "parser.yy" // lalr1.cc:617
+#line 71 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 328 "parser.cc" // lalr1.cc:617
+#line 342 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 17: // "integer"
+      case 21: // "integer"
 
 
-#line 63 "parser.yy" // lalr1.cc:617
+#line 71 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 335 "parser.cc" // lalr1.cc:617
+#line 349 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 18: // "constant hexstring"
+      case 22: // "constant hexstring"
 
 
-#line 63 "parser.yy" // lalr1.cc:617
+#line 71 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 342 "parser.cc" // lalr1.cc:617
+#line 356 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 19: // "option name"
+      case 23: // "option name"
 
 
-#line 63 "parser.yy" // lalr1.cc:617
+#line 71 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 349 "parser.cc" // lalr1.cc:617
+#line 363 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 20: // TOKEN
+      case 24: // TOKEN
 
 
-#line 63 "parser.yy" // lalr1.cc:617
+#line 71 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
         { yyoutput << yysym.value.template as< std::string > (); }
-#line 356 "parser.cc" // lalr1.cc:617
+#line 370 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 25: // option_code
+      case 29: // option_code
 
 
-#line 63 "parser.yy" // lalr1.cc:617
+#line 71 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< uint16_t > (); }
         { yyoutput << yysym.value.template as< uint16_t > (); }
-#line 363 "parser.cc" // lalr1.cc:617
+#line 377 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
-      case 26: // option_repr_type
+      case 30: // option_repr_type
 
 
-#line 63 "parser.yy" // lalr1.cc:617
+#line 71 "parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< TokenOption::RepresentationType > (); }
         { yyoutput << yysym.value.template as< TokenOption::RepresentationType > (); }
-#line 370 "parser.cc" // lalr1.cc:617
+#line 384 "parser.cc" // lalr1.cc:636
         break;
         break;
 
 
 
 
@@ -453,9 +467,6 @@ namespace isc { namespace eval {
   int
   int
   EvalParser::parse ()
   EvalParser::parse ()
   {
   {
-    /// Whether yyla contains a lookahead.
-    bool yyempty = true;
-
     // State.
     // State.
     int yyn;
     int yyn;
     /// Length of the RHS of the rule being reduced.
     /// Length of the RHS of the rule being reduced.
@@ -507,7 +518,7 @@ namespace isc { namespace eval {
       goto yydefault;
       goto yydefault;
 
 
     // Read a lookahead token.
     // Read a lookahead token.
-    if (yyempty)
+    if (yyla.empty ())
       {
       {
         YYCDEBUG << "Reading a token: ";
         YYCDEBUG << "Reading a token: ";
         try
         try
@@ -520,7 +531,6 @@ namespace isc { namespace eval {
             error (yyexc);
             error (yyexc);
             goto yyerrlab1;
             goto yyerrlab1;
           }
           }
-        yyempty = false;
       }
       }
     YY_SYMBOL_PRINT ("Next token is", yyla);
     YY_SYMBOL_PRINT ("Next token is", yyla);
 
 
@@ -540,9 +550,6 @@ namespace isc { namespace eval {
         goto yyreduce;
         goto yyreduce;
       }
       }
 
 
-    // Discard the token being shifted.
-    yyempty = true;
-
     // Count tokens shifted since error; after three, turn off error status.
     // Count tokens shifted since error; after three, turn off error status.
     if (yyerrstatus_)
     if (yyerrstatus_)
       --yyerrstatus_;
       --yyerrstatus_;
@@ -573,19 +580,19 @@ namespace isc { namespace eval {
          when using variants.  */
          when using variants.  */
         switch (yyr1_[yyn])
         switch (yyr1_[yyn])
     {
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         yylhs.value.build< TokenOption::RepresentationType > ();
         yylhs.value.build< TokenOption::RepresentationType > ();
         break;
         break;
 
 
-      case 16: // "constant string"
+      case 20: // "constant string"
-      case 17: // "integer"
+      case 21: // "integer"
-      case 18: // "constant hexstring"
+      case 22: // "constant hexstring"
-      case 19: // "option name"
+      case 23: // "option name"
-      case 20: // TOKEN
+      case 24: // TOKEN
         yylhs.value.build< std::string > ();
         yylhs.value.build< std::string > ();
         break;
         break;
 
 
-      case 25: // option_code
+      case 29: // option_code
         yylhs.value.build< uint16_t > ();
         yylhs.value.build< uint16_t > ();
         break;
         break;
 
 
@@ -606,44 +613,80 @@ namespace isc { namespace eval {
         {
         {
           switch (yyn)
           switch (yyn)
             {
             {
-  case 3:
+  case 4:
-#line 76 "parser.yy" // lalr1.cc:847
+#line 85 "parser.yy" // lalr1.cc:859
+    {
+                    TokenPtr neg(new TokenNot());
+                    ctx.expression.push_back(neg);
+                }
+#line 623 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 5:
+#line 90 "parser.yy" // lalr1.cc:859
+    {
+                    TokenPtr neg(new TokenAnd());
+                    ctx.expression.push_back(neg);
+                }
+#line 632 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 6:
+#line 95 "parser.yy" // lalr1.cc:859
+    {
+                    TokenPtr neg(new TokenOr());
+                    ctx.expression.push_back(neg);
+                }
+#line 641 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 7:
+#line 100 "parser.yy" // lalr1.cc:859
     {
     {
                     TokenPtr eq(new TokenEqual());
                     TokenPtr eq(new TokenEqual());
                     ctx.expression.push_back(eq);
                     ctx.expression.push_back(eq);
                 }
                 }
-#line 616 "parser.cc" // lalr1.cc:847
+#line 650 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 4:
+  case 8:
-#line 83 "parser.yy" // lalr1.cc:847
+#line 105 "parser.yy" // lalr1.cc:859
+    {
+                    TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), TokenOption::EXISTS));
+                    ctx.expression.push_back(opt);
+                }
+#line 659 "parser.cc" // lalr1.cc:859
+    break;
+
+  case 9:
+#line 112 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                       ctx.expression.push_back(str);
                   }
                   }
-#line 625 "parser.cc" // lalr1.cc:847
+#line 668 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 5:
+  case 10:
-#line 88 "parser.yy" // lalr1.cc:847
+#line 117 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
                       TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(hex);
                       ctx.expression.push_back(hex);
                   }
                   }
-#line 634 "parser.cc" // lalr1.cc:847
+#line 677 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 6:
+  case 11:
-#line 93 "parser.yy" // lalr1.cc:847
+#line 122 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), yystack_[0].value.as< TokenOption::RepresentationType > ()));
                       TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), yystack_[0].value.as< TokenOption::RepresentationType > ()));
                       ctx.expression.push_back(opt);
                       ctx.expression.push_back(opt);
                   }
                   }
-#line 643 "parser.cc" // lalr1.cc:847
+#line 686 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 7:
+  case 12:
-#line 98 "parser.yy" // lalr1.cc:847
+#line 127 "parser.yy" // lalr1.cc:859
     {
     {
                      switch (ctx.getUniverse()) {
                      switch (ctx.getUniverse()) {
                      case Option::V4:
                      case Option::V4:
@@ -663,79 +706,79 @@ namespace isc { namespace eval {
                          error(yystack_[5].location, "relay4 can only be used in DHCPv4.");
                          error(yystack_[5].location, "relay4 can only be used in DHCPv4.");
                      }
                      }
                   }
                   }
-#line 667 "parser.cc" // lalr1.cc:847
+#line 710 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 8:
+  case 13:
-#line 118 "parser.yy" // lalr1.cc:847
+#line 147 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr sub(new TokenSubstring());
                       TokenPtr sub(new TokenSubstring());
                       ctx.expression.push_back(sub);
                       ctx.expression.push_back(sub);
                   }
                   }
-#line 676 "parser.cc" // lalr1.cc:847
+#line 719 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 10:
+  case 15:
-#line 127 "parser.yy" // lalr1.cc:847
+#line 156 "parser.yy" // lalr1.cc:859
     {
     {
                      yylhs.value.as< uint16_t > () = ctx.convertOptionCode(yystack_[0].value.as< std::string > (), yystack_[0].location);
                      yylhs.value.as< uint16_t > () = ctx.convertOptionCode(yystack_[0].value.as< std::string > (), yystack_[0].location);
                  }
                  }
-#line 684 "parser.cc" // lalr1.cc:847
+#line 727 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 11:
+  case 16:
-#line 131 "parser.yy" // lalr1.cc:847
+#line 160 "parser.yy" // lalr1.cc:859
     {
     {
                      yylhs.value.as< uint16_t > () = ctx.convertOptionName(yystack_[0].value.as< std::string > (), yystack_[0].location);
                      yylhs.value.as< uint16_t > () = ctx.convertOptionName(yystack_[0].value.as< std::string > (), yystack_[0].location);
                  }
                  }
-#line 692 "parser.cc" // lalr1.cc:847
+#line 735 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 12:
+  case 17:
-#line 137 "parser.yy" // lalr1.cc:847
+#line 166 "parser.yy" // lalr1.cc:859
     {
     {
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
                       }
                       }
-#line 700 "parser.cc" // lalr1.cc:847
+#line 743 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 13:
+  case 18:
-#line 141 "parser.yy" // lalr1.cc:847
+#line 170 "parser.yy" // lalr1.cc:859
     {
     {
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
                       }
                       }
-#line 708 "parser.cc" // lalr1.cc:847
+#line 751 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 14:
+  case 19:
-#line 147 "parser.yy" // lalr1.cc:847
+#line 176 "parser.yy" // lalr1.cc:859
     {
     {
                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                      ctx.expression.push_back(str);
                      ctx.expression.push_back(str);
                  }
                  }
-#line 717 "parser.cc" // lalr1.cc:847
+#line 760 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 15:
+  case 20:
-#line 154 "parser.yy" // lalr1.cc:847
+#line 183 "parser.yy" // lalr1.cc:859
     {
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                       ctx.expression.push_back(str);
                   }
                   }
-#line 726 "parser.cc" // lalr1.cc:847
+#line 769 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
-  case 16:
+  case 21:
-#line 159 "parser.yy" // lalr1.cc:847
+#line 188 "parser.yy" // lalr1.cc:859
     {
     {
                      TokenPtr str(new TokenString("all"));
                      TokenPtr str(new TokenString("all"));
                      ctx.expression.push_back(str);
                      ctx.expression.push_back(str);
                  }
                  }
-#line 735 "parser.cc" // lalr1.cc:847
+#line 778 "parser.cc" // lalr1.cc:859
     break;
     break;
 
 
 
 
-#line 739 "parser.cc" // lalr1.cc:847
+#line 782 "parser.cc" // lalr1.cc:859
             default:
             default:
               break;
               break;
             }
             }
@@ -763,8 +806,7 @@ namespace isc { namespace eval {
     if (!yyerrstatus_)
     if (!yyerrstatus_)
       {
       {
         ++yynerrs_;
         ++yynerrs_;
-        error (yyla.location, yysyntax_error_ (yystack_[0].state,
+        error (yyla.location, yysyntax_error_ (yystack_[0].state, yyla));
-                                           yyempty ? yyempty_ : yyla.type_get ()));
       }
       }
 
 
 
 
@@ -777,10 +819,10 @@ namespace isc { namespace eval {
         // Return failure if at end of input.
         // Return failure if at end of input.
         if (yyla.type_get () == yyeof_)
         if (yyla.type_get () == yyeof_)
           YYABORT;
           YYABORT;
-        else if (!yyempty)
+        else if (!yyla.empty ())
           {
           {
             yy_destroy_ ("Error: discarding", yyla);
             yy_destroy_ ("Error: discarding", yyla);
-            yyempty = true;
+            yyla.clear ();
           }
           }
       }
       }
 
 
@@ -856,7 +898,7 @@ namespace isc { namespace eval {
     goto yyreturn;
     goto yyreturn;
 
 
   yyreturn:
   yyreturn:
-    if (!yyempty)
+    if (!yyla.empty ())
       yy_destroy_ ("Cleanup: discarding lookahead", yyla);
       yy_destroy_ ("Cleanup: discarding lookahead", yyla);
 
 
     /* Do not reclaim the symbols of the rule whose action triggered
     /* Do not reclaim the symbols of the rule whose action triggered
@@ -876,7 +918,7 @@ namespace isc { namespace eval {
                  << std::endl;
                  << std::endl;
         // Do not try to display the values of the reclaimed symbols,
         // Do not try to display the values of the reclaimed symbols,
         // as their printer might throw an exception.
         // as their printer might throw an exception.
-        if (!yyempty)
+        if (!yyla.empty ())
           yy_destroy_ (YY_NULLPTR, yyla);
           yy_destroy_ (YY_NULLPTR, yyla);
 
 
         while (1 < yystack_.size ())
         while (1 < yystack_.size ())
@@ -896,9 +938,8 @@ namespace isc { namespace eval {
 
 
   // Generate an error message.
   // Generate an error message.
   std::string
   std::string
-  EvalParser::yysyntax_error_ (state_type yystate, symbol_number_type yytoken) const
+  EvalParser::yysyntax_error_ (state_type yystate, const symbol_type& yyla) const
   {
   {
-    std::string yyres;
     // Number of reported tokens (one for the "unexpected", one per
     // Number of reported tokens (one for the "unexpected", one per
     // "expected").
     // "expected").
     size_t yycount = 0;
     size_t yycount = 0;
@@ -912,7 +953,7 @@ namespace isc { namespace eval {
          the only way this function was invoked is if the default action
          the only way this function was invoked is if the default action
          is an error action.  In that case, don't check for expected
          is an error action.  In that case, don't check for expected
          tokens because there are none.
          tokens because there are none.
-       - The only way there can be no lookahead present (in yytoken) is
+       - The only way there can be no lookahead present (in yyla) is
          if this state is a consistent state with a default action.
          if this state is a consistent state with a default action.
          Thus, detecting the absence of a lookahead is sufficient to
          Thus, detecting the absence of a lookahead is sufficient to
          determine that there is no unexpected or expected token to
          determine that there is no unexpected or expected token to
@@ -932,8 +973,9 @@ namespace isc { namespace eval {
          token that will not be accepted due to an error action in a
          token that will not be accepted due to an error action in a
          later state.
          later state.
     */
     */
-    if (yytoken != yyempty_)
+    if (!yyla.empty ())
       {
       {
+        int yytoken = yyla.type_get ();
         yyarg[yycount++] = yytname_[yytoken];
         yyarg[yycount++] = yytname_[yytoken];
         int yyn = yypact_[yystate];
         int yyn = yypact_[yystate];
         if (!yy_pact_value_is_default_ (yyn))
         if (!yy_pact_value_is_default_ (yyn))
@@ -976,6 +1018,7 @@ namespace isc { namespace eval {
 #undef YYCASE_
 #undef YYCASE_
       }
       }
 
 
+    std::string yyres;
     // Argument number.
     // Argument number.
     size_t yyi = 0;
     size_t yyi = 0;
     for (char const* yyp = yyformat; *yyp; ++yyp)
     for (char const* yyp = yyformat; *yyp; ++yyp)
@@ -990,79 +1033,91 @@ namespace isc { namespace eval {
   }
   }
 
 
 
 
-  const signed char EvalParser::yypact_ninf_ = -14;
+  const signed char EvalParser::yypact_ninf_ = -11;
 
 
   const signed char EvalParser::yytable_ninf_ = -1;
   const signed char EvalParser::yytable_ninf_ = -1;
 
 
   const signed char
   const signed char
   EvalParser::yypact_[] =
   EvalParser::yypact_[] =
   {
   {
-      -4,    -3,     3,    -1,   -14,   -14,   -14,    17,   -14,    15,
+      -4,    -8,     9,    -4,    12,    -4,   -11,   -11,   -11,    29,
-     -13,    -4,   -13,   -14,    -4,   -14,   -14,     4,     9,     6,
+       7,    37,    16,    -1,   -11,    16,     0,   -11,    -4,    -4,
-     -14,    12,     7,    13,     1,   -14,    14,     1,   -14,   -14,
+      -1,   -11,   -11,    23,    25,    26,    28,   -11,   -11,    38,
-     -14,    -7,   -14,   -14,   -14,    16,   -14
+     -11,    30,    16,    31,    32,    22,    34,   -11,    33,    27,
+     -11,   -11,   -11,   -11,    35,    11,   -11,    27,   -11,   -11,
+      39,   -11
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yydefact_[] =
   EvalParser::yydefact_[] =
   {
   {
-       0,     0,     0,     0,     4,     5,     9,     0,     2,     0,
+       0,     0,     0,     0,     0,     0,     9,    10,    14,     0,
-       0,     0,     0,     1,     0,    10,    11,     0,     0,     0,
+       2,     0,     0,     0,     4,     0,     0,     1,     0,     0,
-       3,     0,     0,     0,     0,    14,     0,     0,    12,    13,
+       0,    15,    16,     0,     0,     0,     0,     3,     5,     6,
-       6,     0,     7,    16,    15,     0,     8
+       7,     0,     0,     0,     0,     0,     0,    19,     0,     0,
+      17,    18,     8,    11,     0,     0,    12,     0,    21,    20,
+       0,    13
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yypgoto_[] =
   EvalParser::yypgoto_[] =
   {
   {
-     -14,   -14,   -14,    -6,    18,     0,   -14,   -14
+     -11,   -11,     8,    15,   -10,    18,   -11,   -11
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yydefgoto_[] =
   EvalParser::yydefgoto_[] =
   {
   {
-      -1,     7,     8,     9,    17,    30,    26,    35
+      -1,     9,    10,    11,    23,    43,    38,    50
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yytable_[] =
   EvalParser::yytable_[] =
   {
   {
-       1,     2,    33,     3,    15,    18,    16,    28,    20,    29,
+       1,     2,     3,    24,     2,    26,     4,    18,    19,     4,
-      34,    10,     4,    12,     5,    11,     6,    13,    14,    21,
+      12,    14,     5,    16,    18,    19,     6,    27,     7,     6,
-      22,    23,    24,    27,    25,    31,     0,    32,     0,    36,
+       8,     7,    36,     8,    48,    13,    28,    29,    25,    17,
-      19
+      15,    40,    49,    41,    42,    30,    40,    21,    41,    22,
+      20,    33,    31,    32,    35,    18,    39,    34,    45,    47,
+       0,     0,    37,    44,     0,     0,    51,    46
   };
   };
 
 
   const signed char
   const signed char
   EvalParser::yycheck_[] =
   EvalParser::yycheck_[] =
   {
   {
-       4,     5,     9,     7,    17,    11,    19,     6,    14,     8,
+       4,     5,     6,     4,     5,    15,    10,     7,     8,    10,
-      17,    14,    16,    14,    18,    12,    20,     0,     3,    15,
+      18,     3,    16,     5,     7,     8,    20,    17,    22,    20,
-      11,    15,    10,    10,    17,    11,    -1,    27,    -1,    13,
+      24,    22,    32,    24,    13,    16,    18,    19,    13,     0,
-      12
+      18,     9,    21,    11,    12,    20,     9,    21,    11,    23,
+       3,    15,    19,    18,    14,     7,    14,    19,    15,    14,
+      -1,    -1,    21,    19,    -1,    -1,    17,    39
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yystos_[] =
   EvalParser::yystos_[] =
   {
   {
-       0,     4,     5,     7,    16,    18,    20,    22,    23,    24,
+       0,     4,     5,     6,    10,    16,    20,    22,    24,    26,
-      14,    12,    14,     0,     3,    17,    19,    25,    24,    25,
+      27,    28,    18,    16,    27,    18,    27,     0,     7,     8,
-      24,    15,    11,    15,    10,    17,    27,    10,     6,     8,
+       3,    21,    23,    29,     4,    28,    29,    17,    27,    27,
-      26,    11,    26,     9,    17,    28,    13
+      28,    19,    18,    15,    19,    14,    29,    21,    31,    14,
+       9,    11,    12,    30,    19,    15,    30,    14,    13,    21,
+      32,    17
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yyr1_[] =
   EvalParser::yyr1_[] =
   {
   {
-       0,    21,    22,    23,    24,    24,    24,    24,    24,    24,
+       0,    25,    26,    27,    27,    27,    27,    27,    27,    28,
-      25,    25,    26,    26,    27,    28,    28
+      28,    28,    28,    28,    28,    29,    29,    30,    30,    31,
+      32,    32
   };
   };
 
 
   const unsigned char
   const unsigned char
   EvalParser::yyr2_[] =
   EvalParser::yyr2_[] =
   {
   {
-       0,     2,     1,     3,     1,     1,     6,     6,     8,     1,
+       0,     2,     1,     3,     2,     3,     3,     3,     6,     1,
-       1,     1,     1,     1,     1,     1,     1
+       1,     6,     6,     8,     1,     1,     1,     1,     1,     1,
+       1,     1
   };
   };
 
 
 
 
@@ -1073,10 +1128,11 @@ namespace isc { namespace eval {
   const EvalParser::yytname_[] =
   const EvalParser::yytname_[] =
   {
   {
   "\"end of file\"", "error", "$undefined", "\"==\"", "\"option\"",
   "\"end of file\"", "error", "$undefined", "\"==\"", "\"option\"",
-  "\"substring\"", "\"text\"", "\"relay4\"", "\"hex\"", "\"all\"", "\".\"",
+  "\"substring\"", "\"not\"", "\"and\"", "\"or\"", "\"text\"",
-  "\",\"", "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"",
+  "\"relay4\"", "\"hex\"", "\"exists\"", "\"all\"", "\".\"", "\",\"",
-  "\"integer\"", "\"constant hexstring\"", "\"option name\"", "TOKEN",
+  "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"", "\"integer\"",
-  "$accept", "expression", "bool_expr", "string_expr", "option_code",
+  "\"constant hexstring\"", "\"option name\"", "TOKEN", "$accept",
+  "expression", "bool_expr", "string_expr", "option_code",
   "option_repr_type", "start_expr", "length_expr", YY_NULLPTR
   "option_repr_type", "start_expr", "length_expr", YY_NULLPTR
   };
   };
 
 
@@ -1084,8 +1140,9 @@ namespace isc { namespace eval {
   const unsigned char
   const unsigned char
   EvalParser::yyrline_[] =
   EvalParser::yyrline_[] =
   {
   {
-       0,    72,    72,    75,    82,    87,    92,    97,   117,   122,
+       0,    80,    80,    83,    84,    89,    94,    99,   104,   111,
-     126,   130,   136,   140,   146,   153,   158
+     116,   121,   126,   146,   151,   155,   159,   165,   169,   175,
+     182,   187
   };
   };
 
 
   // Print the state stack on the debug stream.
   // Print the state stack on the debug stream.
@@ -1118,10 +1175,10 @@ namespace isc { namespace eval {
 #endif // YYDEBUG
 #endif // YYDEBUG
 
 
 
 
-#line 13 "parser.yy" // lalr1.cc:1155
+#line 13 "parser.yy" // lalr1.cc:1167
 } } // isc::eval
 } } // isc::eval
-#line 1124 "parser.cc" // lalr1.cc:1155
+#line 1181 "parser.cc" // lalr1.cc:1167
-#line 165 "parser.yy" // lalr1.cc:1156
+#line 194 "parser.yy" // lalr1.cc:1168
 
 
 void
 void
 isc::eval::EvalParser::error(const location_type& loc,
 isc::eval::EvalParser::error(const location_type& loc,

+ 172 - 85
src/lib/eval/parser.h

@@ -1,8 +1,8 @@
-// A Bison parser, made by GNU Bison 3.0.2.
+// A Bison parser, made by GNU Bison 3.0.4.
 
 
 // Skeleton interface for Bison LALR(1) parsers in C++
 // Skeleton interface for Bison LALR(1) parsers in C++
 
 
-// Copyright (C) 2002-2013 Free Software Foundation, Inc.
+// Copyright (C) 2002-2015 Free Software Foundation, Inc.
 
 
 // This program is free software: you can redistribute it and/or modify
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
 // it under the terms of the GNU General Public License as published by
@@ -40,7 +40,7 @@
 #ifndef YY_YY_PARSER_H_INCLUDED
 #ifndef YY_YY_PARSER_H_INCLUDED
 # define YY_YY_PARSER_H_INCLUDED
 # define YY_YY_PARSER_H_INCLUDED
 // //                    "%code requires" blocks.
 // //                    "%code requires" blocks.
-#line 16 "parser.yy" // lalr1.cc:372
+#line 16 "parser.yy" // lalr1.cc:392
 
 
 #include <string>
 #include <string>
 #include <eval/token.h>
 #include <eval/token.h>
@@ -51,13 +51,14 @@
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace isc::eval;
 using namespace isc::eval;
 
 
-#line 55 "parser.h" // lalr1.cc:372
+#line 55 "parser.h" // lalr1.cc:392
 
 
 # include <cassert>
 # include <cassert>
-# include <vector>
+# include <cstdlib> // std::abort
 # include <iostream>
 # include <iostream>
 # include <stdexcept>
 # include <stdexcept>
 # include <string>
 # include <string>
+# include <vector>
 # include "stack.hh"
 # include "stack.hh"
 # include "location.hh"
 # include "location.hh"
 #include <typeinfo>
 #include <typeinfo>
@@ -125,9 +126,9 @@ using namespace isc::eval;
 # define YYDEBUG 1
 # define YYDEBUG 1
 #endif
 #endif
 
 
-#line 13 "parser.yy" // lalr1.cc:372
+#line 13 "parser.yy" // lalr1.cc:392
 namespace isc { namespace eval {
 namespace isc { namespace eval {
-#line 131 "parser.h" // lalr1.cc:372
+#line 132 "parser.h" // lalr1.cc:392
 
 
 
 
 
 
@@ -144,13 +145,13 @@ namespace isc { namespace eval {
 
 
     /// Empty construction.
     /// Empty construction.
     variant ()
     variant ()
-      : yytname_ (YY_NULLPTR)
+      : yytypeid_ (YY_NULLPTR)
     {}
     {}
 
 
     /// Construct and fill.
     /// Construct and fill.
     template <typename T>
     template <typename T>
     variant (const T& t)
     variant (const T& t)
-      : yytname_ (typeid (T).name ())
+      : yytypeid_ (&typeid (T))
     {
     {
       YYASSERT (sizeof (T) <= S);
       YYASSERT (sizeof (T) <= S);
       new (yyas_<T> ()) T (t);
       new (yyas_<T> ()) T (t);
@@ -159,7 +160,7 @@ namespace isc { namespace eval {
     /// Destruction, allowed only if empty.
     /// Destruction, allowed only if empty.
     ~variant ()
     ~variant ()
     {
     {
-      YYASSERT (!yytname_);
+      YYASSERT (!yytypeid_);
     }
     }
 
 
     /// Instantiate an empty \a T in here.
     /// Instantiate an empty \a T in here.
@@ -167,9 +168,9 @@ namespace isc { namespace eval {
     T&
     T&
     build ()
     build ()
     {
     {
-      YYASSERT (!yytname_);
+      YYASSERT (!yytypeid_);
       YYASSERT (sizeof (T) <= S);
       YYASSERT (sizeof (T) <= S);
-      yytname_ = typeid (T).name ();
+      yytypeid_ = & typeid (T);
       return *new (yyas_<T> ()) T;
       return *new (yyas_<T> ()) T;
     }
     }
 
 
@@ -178,9 +179,9 @@ namespace isc { namespace eval {
     T&
     T&
     build (const T& t)
     build (const T& t)
     {
     {
-      YYASSERT (!yytname_);
+      YYASSERT (!yytypeid_);
       YYASSERT (sizeof (T) <= S);
       YYASSERT (sizeof (T) <= S);
-      yytname_ = typeid (T).name ();
+      yytypeid_ = & typeid (T);
       return *new (yyas_<T> ()) T (t);
       return *new (yyas_<T> ()) T (t);
     }
     }
 
 
@@ -189,7 +190,7 @@ namespace isc { namespace eval {
     T&
     T&
     as ()
     as ()
     {
     {
-      YYASSERT (yytname_ == typeid (T).name ());
+      YYASSERT (*yytypeid_ == typeid (T));
       YYASSERT (sizeof (T) <= S);
       YYASSERT (sizeof (T) <= S);
       return *yyas_<T> ();
       return *yyas_<T> ();
     }
     }
@@ -199,7 +200,7 @@ namespace isc { namespace eval {
     const T&
     const T&
     as () const
     as () const
     {
     {
-      YYASSERT (yytname_ == typeid (T).name ());
+      YYASSERT (*yytypeid_ == typeid (T));
       YYASSERT (sizeof (T) <= S);
       YYASSERT (sizeof (T) <= S);
       return *yyas_<T> ();
       return *yyas_<T> ();
     }
     }
@@ -216,8 +217,8 @@ namespace isc { namespace eval {
     void
     void
     swap (self_type& other)
     swap (self_type& other)
     {
     {
-      YYASSERT (yytname_);
+      YYASSERT (yytypeid_);
-      YYASSERT (yytname_ == other.yytname_);
+      YYASSERT (*yytypeid_ == *other.yytypeid_);
       std::swap (as<T> (), other.as<T> ());
       std::swap (as<T> (), other.as<T> ());
     }
     }
 
 
@@ -247,7 +248,7 @@ namespace isc { namespace eval {
     destroy ()
     destroy ()
     {
     {
       as<T> ().~T ();
       as<T> ().~T ();
-      yytname_ = YY_NULLPTR;
+      yytypeid_ = YY_NULLPTR;
     }
     }
 
 
   private:
   private:
@@ -282,7 +283,7 @@ namespace isc { namespace eval {
     } yybuffer_;
     } yybuffer_;
 
 
     /// Whether the content is built: if defined, the name of the stored type.
     /// Whether the content is built: if defined, the name of the stored type.
-    const char *yytname_;
+    const std::type_info *yytypeid_;
   };
   };
 
 
 
 
@@ -332,30 +333,37 @@ namespace isc { namespace eval {
         TOKEN_EQUAL = 258,
         TOKEN_EQUAL = 258,
         TOKEN_OPTION = 259,
         TOKEN_OPTION = 259,
         TOKEN_SUBSTRING = 260,
         TOKEN_SUBSTRING = 260,
-        TOKEN_TEXT = 261,
+        TOKEN_NOT = 261,
-        TOKEN_RELAY4 = 262,
+        TOKEN_AND = 262,
-        TOKEN_HEX = 263,
+        TOKEN_OR = 263,
-        TOKEN_ALL = 264,
+        TOKEN_TEXT = 264,
-        TOKEN_DOT = 265,
+        TOKEN_RELAY4 = 265,
-        TOKEN_COMA = 266,
+        TOKEN_HEX = 266,
-        TOKEN_LPAREN = 267,
+        TOKEN_EXISTS = 267,
-        TOKEN_RPAREN = 268,
+        TOKEN_ALL = 268,
-        TOKEN_LBRACKET = 269,
+        TOKEN_DOT = 269,
-        TOKEN_RBRACKET = 270,
+        TOKEN_COMA = 270,
-        TOKEN_STRING = 271,
+        TOKEN_LPAREN = 271,
-        TOKEN_INTEGER = 272,
+        TOKEN_RPAREN = 272,
-        TOKEN_HEXSTRING = 273,
+        TOKEN_LBRACKET = 273,
-        TOKEN_OPTION_NAME = 274,
+        TOKEN_RBRACKET = 274,
-        TOKEN_TOKEN = 275
+        TOKEN_STRING = 275,
+        TOKEN_INTEGER = 276,
+        TOKEN_HEXSTRING = 277,
+        TOKEN_OPTION_NAME = 278,
+        TOKEN_TOKEN = 279
       };
       };
     };
     };
 
 
     /// (External) token type, as returned by yylex.
     /// (External) token type, as returned by yylex.
     typedef token::yytokentype token_type;
     typedef token::yytokentype token_type;
 
 
-    /// Internal symbol number.
+    /// Symbol type: an internal symbol number.
     typedef int symbol_number_type;
     typedef int symbol_number_type;
 
 
+    /// The symbol type number to denote an empty symbol.
+    enum { empty_symbol = -2 };
+
     /// Internal symbol number for tokens (subsumed by symbol_number_type).
     /// Internal symbol number for tokens (subsumed by symbol_number_type).
     typedef unsigned char token_number_type;
     typedef unsigned char token_number_type;
 
 
@@ -393,8 +401,15 @@ namespace isc { namespace eval {
                     const semantic_type& v,
                     const semantic_type& v,
                     const location_type& l);
                     const location_type& l);
 
 
+      /// Destroy the symbol.
       ~basic_symbol ();
       ~basic_symbol ();
 
 
+      /// Destroy contents, and record that is empty.
+      void clear ();
+
+      /// Whether empty.
+      bool empty () const;
+
       /// Destructive move, \a s is emptied into this.
       /// Destructive move, \a s is emptied into this.
       void move (basic_symbol& s);
       void move (basic_symbol& s);
 
 
@@ -424,21 +439,23 @@ namespace isc { namespace eval {
       /// Constructor from (external) token numbers.
       /// Constructor from (external) token numbers.
       by_type (kind_type t);
       by_type (kind_type t);
 
 
+      /// Record that this symbol is empty.
+      void clear ();
+
       /// Steal the symbol type from \a that.
       /// Steal the symbol type from \a that.
       void move (by_type& that);
       void move (by_type& that);
 
 
       /// The (internal) type number (corresponding to \a type).
       /// The (internal) type number (corresponding to \a type).
-      /// -1 when this symbol is empty.
+      /// \a empty when empty.
       symbol_number_type type_get () const;
       symbol_number_type type_get () const;
 
 
       /// The token.
       /// The token.
       token_type token () const;
       token_type token () const;
 
 
-      enum { empty = 0 };
-
       /// The symbol type.
       /// The symbol type.
-      /// -1 when this symbol is empty.
+      /// \a empty_symbol when empty.
-      token_number_type type;
+      /// An int, not token_number_type, to be able to store empty_symbol.
+      int type;
     };
     };
 
 
     /// "External" symbols: returned by the scanner.
     /// "External" symbols: returned by the scanner.
@@ -463,6 +480,18 @@ namespace isc { namespace eval {
 
 
     static inline
     static inline
     symbol_type
     symbol_type
+    make_NOT (const location_type& l);
+
+    static inline
+    symbol_type
+    make_AND (const location_type& l);
+
+    static inline
+    symbol_type
+    make_OR (const location_type& l);
+
+    static inline
+    symbol_type
     make_TEXT (const location_type& l);
     make_TEXT (const location_type& l);
 
 
     static inline
     static inline
@@ -475,6 +504,10 @@ namespace isc { namespace eval {
 
 
     static inline
     static inline
     symbol_type
     symbol_type
+    make_EXISTS (const location_type& l);
+
+    static inline
+    symbol_type
     make_ALL (const location_type& l);
     make_ALL (const location_type& l);
 
 
     static inline
     static inline
@@ -562,9 +595,9 @@ namespace isc { namespace eval {
 
 
     /// Generate an error message.
     /// Generate an error message.
     /// \param yystate   the state where the error occurred.
     /// \param yystate   the state where the error occurred.
-    /// \param yytoken   the lookahead token type, or yyempty_.
+    /// \param yyla      the lookahead token.
     virtual std::string yysyntax_error_ (state_type yystate,
     virtual std::string yysyntax_error_ (state_type yystate,
-                                         symbol_number_type yytoken) const;
+                                         const symbol_type& yyla) const;
 
 
     /// Compute post-reduction state.
     /// Compute post-reduction state.
     /// \param yystate   the current state
     /// \param yystate   the current state
@@ -667,16 +700,21 @@ namespace isc { namespace eval {
       /// Copy constructor.
       /// Copy constructor.
       by_state (const by_state& other);
       by_state (const by_state& other);
 
 
+      /// Record that this symbol is empty.
+      void clear ();
+
       /// Steal the symbol type from \a that.
       /// Steal the symbol type from \a that.
       void move (by_state& that);
       void move (by_state& that);
 
 
       /// The (internal) type number (corresponding to \a state).
       /// The (internal) type number (corresponding to \a state).
-      /// "empty" when empty.
+      /// \a empty_symbol when empty.
       symbol_number_type type_get () const;
       symbol_number_type type_get () const;
 
 
-      enum { empty = 0 };
+      /// The state number used to denote an empty symbol.
+      enum { empty_state = -1 };
 
 
       /// The state.
       /// The state.
+      /// \a empty when empty.
       state_type state;
       state_type state;
     };
     };
 
 
@@ -717,17 +755,16 @@ namespace isc { namespace eval {
     /// Pop \a n symbols the three stacks.
     /// Pop \a n symbols the three stacks.
     void yypop_ (unsigned int n = 1);
     void yypop_ (unsigned int n = 1);
 
 
-    // Constants.
+    /// Constants.
     enum
     enum
     {
     {
       yyeof_ = 0,
       yyeof_ = 0,
-      yylast_ = 30,     ///< Last index in yytable_.
+      yylast_ = 57,     ///< Last index in yytable_.
       yynnts_ = 8,  ///< Number of nonterminal symbols.
       yynnts_ = 8,  ///< Number of nonterminal symbols.
-      yyempty_ = -2,
+      yyfinal_ = 17, ///< Termination state number.
-      yyfinal_ = 13, ///< Termination state number.
       yyterror_ = 1,
       yyterror_ = 1,
       yyerrcode_ = 256,
       yyerrcode_ = 256,
-      yyntokens_ = 21  ///< Number of tokens.
+      yyntokens_ = 25  ///< Number of tokens.
     };
     };
 
 
 
 
@@ -771,9 +808,9 @@ 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,
        2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
        5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
        5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
-      15,    16,    17,    18,    19,    20
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24
     };
     };
-    const unsigned int user_token_number_max_ = 275;
+    const unsigned int user_token_number_max_ = 279;
     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_)
@@ -806,19 +843,19 @@ namespace isc { namespace eval {
   {
   {
       switch (other.type_get ())
       switch (other.type_get ())
     {
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.copy< TokenOption::RepresentationType > (other.value);
         value.copy< TokenOption::RepresentationType > (other.value);
         break;
         break;
 
 
-      case 16: // "constant string"
+      case 20: // "constant string"
-      case 17: // "integer"
+      case 21: // "integer"
-      case 18: // "constant hexstring"
+      case 22: // "constant hexstring"
-      case 19: // "option name"
+      case 23: // "option name"
-      case 20: // TOKEN
+      case 24: // TOKEN
         value.copy< std::string > (other.value);
         value.copy< std::string > (other.value);
         break;
         break;
 
 
-      case 25: // option_code
+      case 29: // option_code
         value.copy< uint16_t > (other.value);
         value.copy< uint16_t > (other.value);
         break;
         break;
 
 
@@ -839,19 +876,19 @@ namespace isc { namespace eval {
     (void) v;
     (void) v;
       switch (this->type_get ())
       switch (this->type_get ())
     {
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.copy< TokenOption::RepresentationType > (v);
         value.copy< TokenOption::RepresentationType > (v);
         break;
         break;
 
 
-      case 16: // "constant string"
+      case 20: // "constant string"
-      case 17: // "integer"
+      case 21: // "integer"
-      case 18: // "constant hexstring"
+      case 22: // "constant hexstring"
-      case 19: // "option name"
+      case 23: // "option name"
-      case 20: // TOKEN
+      case 24: // TOKEN
         value.copy< std::string > (v);
         value.copy< std::string > (v);
         break;
         break;
 
 
-      case 25: // option_code
+      case 29: // option_code
         value.copy< uint16_t > (v);
         value.copy< uint16_t > (v);
         break;
         break;
 
 
@@ -896,8 +933,18 @@ namespace isc { namespace eval {
   inline
   inline
   EvalParser::basic_symbol<Base>::~basic_symbol ()
   EvalParser::basic_symbol<Base>::~basic_symbol ()
   {
   {
+    clear ();
+  }
+
+  template <typename Base>
+  inline
+  void
+  EvalParser::basic_symbol<Base>::clear ()
+  {
     // User destructor.
     // User destructor.
     symbol_number_type yytype = this->type_get ();
     symbol_number_type yytype = this->type_get ();
+    basic_symbol<Base>& yysym = *this;
+    (void) yysym;
     switch (yytype)
     switch (yytype)
     {
     {
    default:
    default:
@@ -907,19 +954,19 @@ namespace isc { namespace eval {
     // Type destructor.
     // Type destructor.
     switch (yytype)
     switch (yytype)
     {
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.template destroy< TokenOption::RepresentationType > ();
         value.template destroy< TokenOption::RepresentationType > ();
         break;
         break;
 
 
-      case 16: // "constant string"
+      case 20: // "constant string"
-      case 17: // "integer"
+      case 21: // "integer"
-      case 18: // "constant hexstring"
+      case 22: // "constant hexstring"
-      case 19: // "option name"
+      case 23: // "option name"
-      case 20: // TOKEN
+      case 24: // TOKEN
         value.template destroy< std::string > ();
         value.template destroy< std::string > ();
         break;
         break;
 
 
-      case 25: // option_code
+      case 29: // option_code
         value.template destroy< uint16_t > ();
         value.template destroy< uint16_t > ();
         break;
         break;
 
 
@@ -927,6 +974,15 @@ namespace isc { namespace eval {
         break;
         break;
     }
     }
 
 
+    Base::clear ();
+  }
+
+  template <typename Base>
+  inline
+  bool
+  EvalParser::basic_symbol<Base>::empty () const
+  {
+    return Base::type_get () == empty_symbol;
   }
   }
 
 
   template <typename Base>
   template <typename Base>
@@ -937,19 +993,19 @@ namespace isc { namespace eval {
     super_type::move(s);
     super_type::move(s);
       switch (this->type_get ())
       switch (this->type_get ())
     {
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.move< TokenOption::RepresentationType > (s.value);
         value.move< TokenOption::RepresentationType > (s.value);
         break;
         break;
 
 
-      case 16: // "constant string"
+      case 20: // "constant string"
-      case 17: // "integer"
+      case 21: // "integer"
-      case 18: // "constant hexstring"
+      case 22: // "constant hexstring"
-      case 19: // "option name"
+      case 23: // "option name"
-      case 20: // TOKEN
+      case 24: // TOKEN
         value.move< std::string > (s.value);
         value.move< std::string > (s.value);
         break;
         break;
 
 
-      case 25: // option_code
+      case 29: // option_code
         value.move< uint16_t > (s.value);
         value.move< uint16_t > (s.value);
         break;
         break;
 
 
@@ -963,7 +1019,7 @@ namespace isc { namespace eval {
   // by_type.
   // by_type.
   inline
   inline
   EvalParser::by_type::by_type ()
   EvalParser::by_type::by_type ()
-     : type (empty)
+    : type (empty_symbol)
   {}
   {}
 
 
   inline
   inline
@@ -978,10 +1034,17 @@ namespace isc { namespace eval {
 
 
   inline
   inline
   void
   void
+  EvalParser::by_type::clear ()
+  {
+    type = empty_symbol;
+  }
+
+  inline
+  void
   EvalParser::by_type::move (by_type& that)
   EvalParser::by_type::move (by_type& that)
   {
   {
     type = that.type;
     type = that.type;
-    that.type = empty;
+    that.clear ();
   }
   }
 
 
   inline
   inline
@@ -1003,7 +1066,7 @@ namespace isc { namespace eval {
     {
     {
        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,   271,   272,   273,   274,
      265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
-     275
+     275,   276,   277,   278,   279
     };
     };
     return static_cast<token_type> (yytoken_number_[type]);
     return static_cast<token_type> (yytoken_number_[type]);
   }
   }
@@ -1033,6 +1096,24 @@ namespace isc { namespace eval {
   }
   }
 
 
   EvalParser::symbol_type
   EvalParser::symbol_type
+  EvalParser::make_NOT (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_NOT, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_AND (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_AND, l);
+  }
+
+  EvalParser::symbol_type
+  EvalParser::make_OR (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_OR, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_TEXT (const location_type& l)
   EvalParser::make_TEXT (const location_type& l)
   {
   {
     return symbol_type (token::TOKEN_TEXT, l);
     return symbol_type (token::TOKEN_TEXT, l);
@@ -1051,6 +1132,12 @@ namespace isc { namespace eval {
   }
   }
 
 
   EvalParser::symbol_type
   EvalParser::symbol_type
+  EvalParser::make_EXISTS (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_EXISTS, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_ALL (const location_type& l)
   EvalParser::make_ALL (const location_type& l)
   {
   {
     return symbol_type (token::TOKEN_ALL, l);
     return symbol_type (token::TOKEN_ALL, l);
@@ -1123,9 +1210,9 @@ namespace isc { namespace eval {
   }
   }
 
 
 
 
-#line 13 "parser.yy" // lalr1.cc:372
+#line 13 "parser.yy" // lalr1.cc:392
 } } // isc::eval
 } } // isc::eval
-#line 1129 "parser.h" // lalr1.cc:372
+#line 1216 "parser.h" // lalr1.cc:392
 
 
 
 
 
 

+ 31 - 2
src/lib/eval/parser.yy

@@ -39,9 +39,13 @@ using namespace isc::eval;
   EQUAL "=="
   EQUAL "=="
   OPTION "option"
   OPTION "option"
   SUBSTRING "substring"
   SUBSTRING "substring"
+  NOT "not"
+  AND "and"
+  OR "or"
   TEXT "text"
   TEXT "text"
   RELAY4 "relay4"
   RELAY4 "relay4"
   HEX "hex"
   HEX "hex"
+  EXISTS "exists"
   ALL "all"
   ALL "all"
   DOT "."
   DOT "."
   COMA ","
   COMA ","
@@ -60,6 +64,10 @@ using namespace isc::eval;
 %type <uint16_t> option_code
 %type <uint16_t> option_code
 %type <TokenOption::RepresentationType> option_repr_type
 %type <TokenOption::RepresentationType> option_repr_type
 
 
+%left OR
+%left AND
+%precedence NOT
+
 %printer { yyoutput << $$; } <*>;
 %printer { yyoutput << $$; } <*>;
 
 
 %%
 %%
@@ -72,11 +80,32 @@ using namespace isc::eval;
 expression : bool_expr
 expression : bool_expr
            ;
            ;
 
 
-bool_expr : string_expr EQUAL string_expr
+bool_expr : "(" bool_expr ")"
+          | NOT bool_expr
+                {
+                    TokenPtr neg(new TokenNot());
+                    ctx.expression.push_back(neg);
+                }
+          | bool_expr AND bool_expr
+                {
+                    TokenPtr neg(new TokenAnd());
+                    ctx.expression.push_back(neg);
+                }
+          | bool_expr OR bool_expr
+                {
+                    TokenPtr neg(new TokenOr());
+                    ctx.expression.push_back(neg);
+                }
+          | string_expr EQUAL string_expr
                 {
                 {
                     TokenPtr eq(new TokenEqual());
                     TokenPtr eq(new TokenEqual());
                     ctx.expression.push_back(eq);
                     ctx.expression.push_back(eq);
                 }
                 }
+          | OPTION "[" option_code "]" "." EXISTS
+                {
+                    TokenPtr opt(new TokenOption($3, TokenOption::EXISTS));
+                    ctx.expression.push_back(opt);
+                }
           ;
           ;
 
 
 string_expr : STRING
 string_expr : STRING
@@ -120,7 +149,7 @@ string_expr : STRING
                       ctx.expression.push_back(sub);
                       ctx.expression.push_back(sub);
                   }
                   }
             | TOKEN
             | TOKEN
-                // Temporary unused token to avoid explict but long errors
+                // Temporary unused token to avoid explicit but long errors
             ;
             ;
 
 
 option_code : INTEGER
 option_code : INTEGER

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

@@ -1,4 +1,5 @@
-// A Bison parser, made by GNU Bison 3.0.2.
+// Generated 20160219
+// A Bison parser, made by GNU Bison 3.0.4.
 
 
 // Positions for Bison parsers in C++
 // Positions for Bison parsers in C++
 
 

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

@@ -1,4 +1,5 @@
-// A Bison parser, made by GNU Bison 3.0.2.
+// Generated 20160219
+// A Bison parser, made by GNU Bison 3.0.4.
 
 
 // Stack handling for Bison parsers in C++
 // Stack handling for Bison parsers in C++
 
 

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

@@ -26,7 +26,8 @@ if HAVE_GTEST
 
 
 TESTS += libeval_unittests
 TESTS += libeval_unittests
 
 
-libeval_unittests_SOURCES  = context_unittest.cc
+libeval_unittests_SOURCES  = boolean_unittest.cc
+libeval_unittests_SOURCES += context_unittest.cc
 libeval_unittests_SOURCES += evaluate_unittest.cc
 libeval_unittests_SOURCES += evaluate_unittest.cc
 libeval_unittests_SOURCES += token_unittest.cc
 libeval_unittests_SOURCES += token_unittest.cc
 libeval_unittests_SOURCES += run_unittests.cc
 libeval_unittests_SOURCES += run_unittests.cc

+ 72 - 0
src/lib/eval/tests/boolean_unittest.cc

@@ -0,0 +1,72 @@
+// Copyright (C) 2016 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.
+
+#include <config.h>
+#include <eval/eval_context.h>
+#include <eval/evaluate.h>
+#include <eval/token.h>
+#include <dhcp/pkt4.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief Test fixture for testing booleans.
+class BooleanTest : public ::testing::Test {
+public:
+    void check(const string& expr, bool expected) {
+        EvalContext eval(Option::V4);
+        ASSERT_TRUE(eval.parseString(expr));
+        Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
+        if (expected) {
+            EXPECT_TRUE(evaluate(eval.expression, *pkt4));
+        } else {
+            EXPECT_FALSE(evaluate(eval.expression, *pkt4));
+        }
+    }
+};
+
+// A group of tests
+TEST_F(BooleanTest, tests) {
+    // true and (false or false)
+    check("('a' == 'a') and (('a' == 'b') or ('b' == 'a'))", false);
+    // (true and false) or false
+    check("(('a' == 'a') and ('a' == 'b')) or ('b' == 'a')", false);
+    // not true
+    check("not ('a' == 'a')", false);
+    // not false
+    check("not ('a' == 'b')", true);
+    // true and true and true and false
+    check("('a' == 'a') and ('b' == 'b') and ('c' == 'c') and ('a' == 'c')",
+          false);
+    // false or false or false or true
+    check("('a' == 'b') or ('a' == 'c') or ('b' == 'c') or ('b' == 'b')",
+          true);
+    // true or false or false or false
+    check("('a' == 'a') or ('a' == 'b') or ('a' == 'c') or ('b' == 'c')",
+          true);
+    // not (true or false)
+    check("not (('a' == 'a') or ('a' == 'b'))", false);
+    // not (true and false)
+    check("not (('a' == 'a') and ('a' == 'b'))", true);
+    // (not true) and false
+    check("(not ('a' == 'a')) and ('a' == 'b')",false);
+}
+
+};

+ 166 - 1
src/lib/eval/tests/context_unittest.cc

@@ -232,6 +232,16 @@ TEST_F(EvalContextTest, optionWithName) {
     checkTokenOption(eval.expression.at(0), 12);
     checkTokenOption(eval.expression.at(0), 12);
 }
 }
 
 
+// Test parsing of an option existence
+TEST_F(EvalContextTest, optionExists) {
+    EvalContext eval(Option::V4);
+
+    EXPECT_NO_THROW(parsed_ = eval.parseString("option[100].exists"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(1, eval.expression.size());
+    checkTokenOption(eval.expression.at(0), 100);
+}
+
 // Test checking that whitespace can surround option name.
 // Test checking that whitespace can surround option name.
 TEST_F(EvalContextTest, optionWithNameAndWhitespace) {
 TEST_F(EvalContextTest, optionWithNameAndWhitespace) {
     EvalContext eval(Option::V4);
     EvalContext eval(Option::V4);
@@ -265,6 +275,112 @@ TEST_F(EvalContextTest, optionHex) {
     checkTokenOption(eval.expression.at(0), 123);
     checkTokenOption(eval.expression.at(0), 123);
 }
 }
 
 
+// Test parsing of logical operators
+TEST_F(EvalContextTest, logicalOps) {
+    // option.exists
+    EvalContext eval0(Option::V4);
+    EXPECT_NO_THROW(parsed_ = eval0.parseString("option[123].exists"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(1, eval0.expression.size());
+    TokenPtr token = eval0.expression.at(0);
+    ASSERT_TRUE(token);
+    boost::shared_ptr<TokenOption> opt =
+        boost::dynamic_pointer_cast<TokenOption>(token);
+    EXPECT_TRUE(opt);
+
+    // not
+    EvalContext evaln(Option::V4);
+    EXPECT_NO_THROW(parsed_ = evaln.parseString("not option[123].exists"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(2, evaln.expression.size());
+    token = evaln.expression.at(1);
+    ASSERT_TRUE(token);
+    boost::shared_ptr<TokenNot> tnot =
+        boost::dynamic_pointer_cast<TokenNot>(token);
+    EXPECT_TRUE(tnot);
+
+    // and
+    EvalContext evala(Option::V4);
+    EXPECT_NO_THROW(parsed_ =
+        evala.parseString("option[123].exists and option[123].exists"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(3, evala.expression.size());
+    token = evala.expression.at(2);
+    ASSERT_TRUE(token);
+    boost::shared_ptr<TokenAnd> tand =
+        boost::dynamic_pointer_cast<TokenAnd>(token);
+    EXPECT_TRUE(tand);
+
+    // or
+    EvalContext evalo(Option::V4);
+    EXPECT_NO_THROW(parsed_ =
+        evalo.parseString("option[123].exists or option[123].exists"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(3, evalo.expression.size());
+    token = evalo.expression.at(2);
+    ASSERT_TRUE(token);
+    boost::shared_ptr<TokenOr> tor =
+        boost::dynamic_pointer_cast<TokenOr>(token);
+    EXPECT_TRUE(tor);
+}
+
+// Test parsing of logical operators with precedence
+TEST_F(EvalContextTest, logicalPrecedence) {
+    // not precedence > and precedence
+    EvalContext evalna(Option::V4);
+    EXPECT_NO_THROW(parsed_ =
+        evalna.parseString("not option[123].exists and option[123].exists"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(4, evalna.expression.size());
+    TokenPtr token = evalna.expression.at(3);
+    ASSERT_TRUE(token);
+    boost::shared_ptr<TokenAnd> tand =
+        boost::dynamic_pointer_cast<TokenAnd>(token);
+    EXPECT_TRUE(tand);
+
+    // and precedence > or precedence
+    EvalContext evaloa(Option::V4);
+    EXPECT_NO_THROW(parsed_ =
+        evaloa.parseString("option[123].exists or option[123].exists "
+                         "and option[123].exists"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(5, evaloa.expression.size());
+    token = evaloa.expression.at(4);
+    ASSERT_TRUE(token);
+    boost::shared_ptr<TokenOr> tor =
+        boost::dynamic_pointer_cast<TokenOr>(token);
+    EXPECT_TRUE(tor);
+}
+
+// Test parsing of logical operators with parentheses (same than
+// with precedence but using parentheses to overwrite precedence)
+TEST_F(EvalContextTest, logicalParentheses) {
+    // not precedence > and precedence
+    EvalContext evalna(Option::V4);
+    EXPECT_NO_THROW(parsed_ =
+        evalna.parseString("not (option[123].exists and option[123].exists)"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(4, evalna.expression.size());
+    TokenPtr token = evalna.expression.at(3);
+    ASSERT_TRUE(token);
+    boost::shared_ptr<TokenNot> tnot =
+        boost::dynamic_pointer_cast<TokenNot>(token);
+    EXPECT_TRUE(tnot);
+
+    // and precedence > or precedence
+    EvalContext evaloa(Option::V4);
+    EXPECT_NO_THROW(parsed_ =
+        evaloa.parseString("(option[123].exists or option[123].exists) "
+                         "and option[123].exists"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(5, evaloa.expression.size());
+    token = evaloa.expression.at(4);
+    ASSERT_TRUE(token);
+    boost::shared_ptr<TokenAnd> tand =
+        boost::dynamic_pointer_cast<TokenAnd>(token);
+    EXPECT_TRUE(tand);
+}
+
 // Test the parsing of a substring expression
 // Test the parsing of a substring expression
 TEST_F(EvalContextTest, substring) {
 TEST_F(EvalContextTest, substring) {
     EvalContext eval(Option::V4);
     EvalContext eval(Option::V4);
@@ -369,9 +485,45 @@ TEST_F(EvalContextTest, parseErrors) {
     checkError("'foo''bar'",
     checkError("'foo''bar'",
                "<string>:1.6-10: syntax error, unexpected constant string, "
                "<string>:1.6-10: syntax error, unexpected constant string, "
                "expecting ==");
                "expecting ==");
+    checkError("'foo' (",
+               "<string>:1.7: syntax error, unexpected (, expecting ==");
     checkError("== 'ab'", "<string>:1.1-2: syntax error, unexpected ==");
     checkError("== 'ab'", "<string>:1.1-2: syntax error, unexpected ==");
     checkError("'foo' ==",
     checkError("'foo' ==",
                "<string>:1.9: syntax error, unexpected end of file");
                "<string>:1.9: syntax error, unexpected end of file");
+    checkError("('foo' == 'bar'",
+               "<string>:1.16: syntax error, unexpected end of file, "
+               "expecting and or or or )");
+    checkError("('foo' == 'bar') ''",
+               "<string>:1.18-19: syntax error, unexpected constant string, "
+               "expecting end of file");
+    checkError("not",
+               "<string>:1.4: syntax error, unexpected end of file");
+    checkError("not 'foo'",
+               "<string>:1.10: syntax error, unexpected end of file, "
+               "expecting ==");
+    checkError("not()",
+               "<string>:1.5: syntax error, unexpected )");
+    checkError("(not('foo' 'bar')",
+               "<string>:1.12-16: syntax error, unexpected constant string, "
+               "expecting ==");
+    checkError("and",
+               "<string>:1.1-3: syntax error, unexpected and");
+    checkError("'foo' and",
+               "<string>:1.7-9: syntax error, unexpected and, expecting ==");
+    checkError("'foo' == 'bar' and",
+               "<string>:1.19: syntax error, unexpected end of file");
+    checkError("'foo' == 'bar' and ''",
+               "<string>:1.22: syntax error, unexpected end of file, "
+               "expecting ==");
+    checkError("or",
+               "<string>:1.1-2: syntax error, unexpected or");
+    checkError("'foo' or",
+               "<string>:1.7-8: syntax error, unexpected or, expecting ==");
+    checkError("'foo' == 'bar' or",
+               "<string>:1.18: syntax error, unexpected end of file");
+    checkError("'foo' == 'bar' or ''",
+               "<string>:1.21: syntax error, unexpected end of file, "
+               "expecting ==");
     checkError("option 'ab'",
     checkError("option 'ab'",
                "<string>:1.8-11: syntax error, unexpected "
                "<string>:1.8-11: syntax error, unexpected "
                "constant string, expecting [");
                "constant string, expecting [");
@@ -390,6 +542,9 @@ TEST_F(EvalContextTest, parseErrors) {
                "expecting integer or option name");
                "expecting integer or option name");
     checkError("option[10].bin", "<string>:1.12: Invalid character: b");
     checkError("option[10].bin", "<string>:1.12: Invalid character: b");
     checkError("option[boot-size].bin", "<string>:1.19: Invalid character: b");
     checkError("option[boot-size].bin", "<string>:1.19: Invalid character: b");
+    checkError("option[10].exists == 'foo'",
+               "<string>:1.19-20: syntax error, unexpected ==, "
+               "expecting end of file");
     checkError("substring('foobar') == 'f'",
     checkError("substring('foobar') == 'f'",
                "<string>:1.19: syntax error, "
                "<string>:1.19: syntax error, "
                "unexpected ), expecting \",\"");
                "unexpected ), expecting \",\"");
@@ -402,7 +557,7 @@ TEST_F(EvalContextTest, parseErrors) {
                "<string>:1.22: Invalid character: a");
                "<string>:1.22: Invalid character: a");
 }
 }
 
 
-// Tests some type error cases (caught only by the strongly typed parser)
+// Tests some type error cases
 TEST_F(EvalContextTest, typeErrors) {
 TEST_F(EvalContextTest, typeErrors) {
     checkError("'foobar'",
     checkError("'foobar'",
                "<string>:1.9: syntax error, unexpected end of file, "
                "<string>:1.9: syntax error, unexpected end of file, "
@@ -413,6 +568,16 @@ TEST_F(EvalContextTest, typeErrors) {
     checkError("substring('foobar',0x32,1) == 'foo'",
     checkError("substring('foobar',0x32,1) == 'foo'",
                "<string>:1.20-23: syntax error, unexpected constant "
                "<string>:1.20-23: syntax error, unexpected constant "
                "hexstring, expecting integer");
                "hexstring, expecting integer");
+    checkError("('foo' == 'bar') == 'false'",
+               "<string>:1.18-19: syntax error, unexpected ==, "
+               "expecting end of file");
+    checkError("not 'true'",
+               "<string>:1.11: syntax error, unexpected end of file, "
+               "expecting ==");
+    checkError("'true' and 'false'",
+               "<string>:1.8-10: syntax error, unexpected and, expecting ==");
+    checkError("'true' or 'false'",
+               "<string>:1.8-9: syntax error, unexpected or, expecting ==");
 }
 }
 
 
 };
 };

+ 27 - 1
src/lib/eval/tests/evaluate_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -182,6 +182,32 @@ TEST_F(EvaluateTest, compare6) {
     EXPECT_FALSE(result_);
     EXPECT_FALSE(result_);
 }
 }
 
 
+// A test using option existence
+TEST_F(EvaluateTest, exists) {
+    TokenPtr toption;
+
+    ASSERT_NO_THROW(toption.reset(new TokenOption(100, TokenOption::EXISTS)));
+    e_.push_back(toption);
+
+    ASSERT_NO_THROW(result_ = evaluate(e_, *pkt4_));
+    EXPECT_TRUE(result_);
+    ASSERT_NO_THROW(result_ = evaluate(e_, *pkt6_));
+    EXPECT_TRUE(result_);
+}
+
+// A test using option non-existence
+TEST_F(EvaluateTest, dontExists) {
+    TokenPtr toption;
+
+    ASSERT_NO_THROW(toption.reset(new TokenOption(101, TokenOption::EXISTS)));
+    e_.push_back(toption);
+
+    ASSERT_NO_THROW(result_ = evaluate(e_, *pkt4_));
+    EXPECT_FALSE(result_);
+    ASSERT_NO_THROW(result_ = evaluate(e_, *pkt6_));
+    EXPECT_FALSE(result_);
+}
+
 // A test using packets.
 // A test using packets.
 TEST_F(EvaluateTest, packet) {
 TEST_F(EvaluateTest, packet) {
     TokenPtr toption;
     TokenPtr toption;

+ 227 - 0
src/lib/eval/tests/token_unittest.cc

@@ -125,6 +125,24 @@ public:
     /// @todo: Add more option types here
     /// @todo: Add more option types here
 };
 };
 
 
+// This tests the toBool() conversions
+TEST_F(TokenTest, toBool) {
+
+    ASSERT_NO_THROW(Token::toBool("true"));
+    EXPECT_TRUE(Token::toBool("true"));
+    ASSERT_NO_THROW(Token::toBool("false"));
+    EXPECT_FALSE(Token::toBool("false"));
+
+    // Token::toBool() is case-sensitive
+    EXPECT_THROW(Token::toBool("True"), EvalTypeError);
+    EXPECT_THROW(Token::toBool("TRUE"), EvalTypeError);
+
+    // Proposed aliases
+    EXPECT_THROW(Token::toBool("1"), EvalTypeError);
+    EXPECT_THROW(Token::toBool("0"), EvalTypeError);
+    EXPECT_THROW(Token::toBool(""), EvalTypeError);
+}
+
 // This simple test checks that a TokenString, representing a constant string,
 // This simple test checks that a TokenString, representing a constant string,
 // can be used in Pkt4 evaluation. (The actual packet is not used)
 // can be used in Pkt4 evaluation. (The actual packet is not used)
 TEST_F(TokenTest, string4) {
 TEST_F(TokenTest, string4) {
@@ -322,6 +340,28 @@ TEST_F(TokenTest, optionHexString4) {
     EXPECT_EQ("hundred4", values_.top());
     EXPECT_EQ("hundred4", values_.top());
 }
 }
 
 
+// This test checks if a token representing an option value is able to check
+// the existence ofthe option from an IPv4 packet.
+TEST_F(TokenTest, optionExistsString4) {
+    TokenPtr found;
+    TokenPtr not_found;
+
+    // The packets we use have option 100 with a string in them.
+    ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::EXISTS)));
+    ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::EXISTS)));
+
+    ASSERT_NO_THROW(found->evaluate(*pkt4_, values_));
+    ASSERT_NO_THROW(not_found->evaluate(*pkt4_, values_));
+
+    // There should be 2 values evaluated.
+    ASSERT_EQ(2, values_.size());
+
+    // This is a stack, so the pop order is inversed.
+    EXPECT_EQ("false", values_.top());
+    values_.pop();
+    EXPECT_EQ("true", values_.top());
+}
+
 // This test checks if a token representing an option value is able to extract
 // This test checks if a token representing an option value is able to extract
 // the option from an IPv6 packet and properly store the option's value.
 // the option from an IPv6 packet and properly store the option's value.
 TEST_F(TokenTest, optionString6) {
 TEST_F(TokenTest, optionString6) {
@@ -379,6 +419,28 @@ TEST_F(TokenTest, optionHexString6) {
     EXPECT_EQ("hundred6", values_.top());
     EXPECT_EQ("hundred6", values_.top());
 }
 }
 
 
+// This test checks if a token representing an option value is able to check
+// the existence ofthe option from an IPv6 packet.
+TEST_F(TokenTest, optionExistsString6) {
+    TokenPtr found;
+    TokenPtr not_found;
+
+    // The packets we use have option 100 with a string in them.
+    ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::EXISTS)));
+    ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::EXISTS)));
+
+    ASSERT_NO_THROW(found->evaluate(*pkt6_, values_));
+    ASSERT_NO_THROW(not_found->evaluate(*pkt6_, values_));
+
+    // There should be 2 values evaluated.
+    ASSERT_EQ(2, values_.size());
+
+    // This is a stack, so the pop order is inversed.
+    EXPECT_EQ("false", values_.top());
+    values_.pop();
+    EXPECT_EQ("true", values_.top());
+}
+
 // This test checks if a token representing an == operator is able to
 // This test checks if a token representing an == operator is able to
 // compare two values (with incorrectly built stack).
 // compare two values (with incorrectly built stack).
 TEST_F(TokenTest, optionEqualInvalid) {
 TEST_F(TokenTest, optionEqualInvalid) {
@@ -426,6 +488,171 @@ TEST_F(TokenTest, optionEqualTrue) {
     EXPECT_EQ("true", values_.top());
     EXPECT_EQ("true", values_.top());
 }
 }
 
 
+// This test checks if a token representing a not is able to
+// negate a boolean value (with incorrectly built stack).
+TEST_F(TokenTest, optionNotInvalid) {
+
+    ASSERT_NO_THROW(t_.reset(new TokenNot()));
+
+    // CASE 1: The stack is empty.
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+    // CASE 2: The top value is not a boolean
+    values_.push("foo");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
+}
+
+// This test checks if a token representing a not operator is able to
+// negate a boolean value.
+TEST_F(TokenTest, optionNot) {
+
+    ASSERT_NO_THROW(t_.reset(new TokenNot()));
+
+    values_.push("true");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+    // After evaluation there should be the negation of the value.
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("false", values_.top());
+
+    // Double negation is identity.
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("true", values_.top());
+}
+
+// This test checks if a token representing an and is able to
+// conjugate two values (with incorrectly built stack).
+TEST_F(TokenTest, optionAndInvalid) {
+
+    ASSERT_NO_THROW(t_.reset(new TokenAnd()));
+
+    // CASE 1: There's not enough values on the stack. and is an operator that
+    // takes two parameters. There are 0 on the stack.
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+    // CASE 2: One value is still not enough.
+    values_.push("foo");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+    // CASE 3: The two values must be logical
+    values_.push("true");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
+
+    // Swap the 2 values
+    values_.push("true");
+    values_.push("foo");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
+}
+
+// This test checks if a token representing an and operator is able to
+// conjugate false with another logical
+TEST_F(TokenTest, optionAndFalse) {
+
+    ASSERT_NO_THROW(t_.reset(new TokenAnd()));
+
+    values_.push("true");
+    values_.push("false");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+    // After evaluation there should be a single "false" value
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("false", values_.top());
+
+    // After true and false, checks false and true
+    values_.push("true");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("false", values_.top());
+
+    // And false and false
+    values_.push("false");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("false", values_.top());
+}
+
+// This test checks if a token representing an and is able to
+// conjugate two true values.
+TEST_F(TokenTest, optionAndTrue) {
+
+    ASSERT_NO_THROW(t_.reset(new TokenAnd()));
+
+    values_.push("true");
+    values_.push("true");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+    // After evaluation there should be a single "true" value
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("true", values_.top());
+}
+
+// This test checks if a token representing an or is able to
+// combinate two values (with incorrectly built stack).
+TEST_F(TokenTest, optionOrInvalid) {
+
+    ASSERT_NO_THROW(t_.reset(new TokenOr()));
+
+    // CASE 1: There's not enough values on the stack. or is an operator that
+    // takes two parameters. There are 0 on the stack.
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+    // CASE 2: One value is still not enough.
+    values_.push("foo");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+    // CASE 3: The two values must be logical
+    values_.push("true");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
+
+    // Swap the 2 values
+    values_.push("true");
+    values_.push("foo");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
+}
+
+// This test checks if a token representing an or is able to
+// conjugate two false values.
+TEST_F(TokenTest, optionOrFalse) {
+
+    ASSERT_NO_THROW(t_.reset(new TokenOr()));
+
+    values_.push("false");
+    values_.push("false");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+    // After evaluation there should be a single "false" value
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("false", values_.top());
+}
+
+// This test checks if a token representing an == operator is able to
+// conjugate true with another logical
+TEST_F(TokenTest, optionOrTrue) {
+
+    ASSERT_NO_THROW(t_.reset(new TokenOr()));
+
+    values_.push("false");
+    values_.push("true");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+    // After evaluation there should be a single "true" value
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("true", values_.top());
+
+    // After false or true, checks true or false
+    values_.push("false");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("true", values_.top());
+
+    // And true or true
+    values_.push("true");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("true", values_.top());
+}
+
 };
 };
 
 
 // This test checks if an a token representing a substring request
 // This test checks if an a token representing a substring request

+ 67 - 1
src/lib/eval/token.cc

@@ -66,13 +66,17 @@ TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
     if (opt) {
     if (opt) {
         if (representation_type_ == TEXTUAL) {
         if (representation_type_ == TEXTUAL) {
             opt_str = opt->toString();
             opt_str = opt->toString();
-        } else {
+        } else if (representation_type_ == HEXADECIMAL) {
             std::vector<uint8_t> binary = opt->toBinary();
             std::vector<uint8_t> binary = opt->toBinary();
             opt_str.resize(binary.size());
             opt_str.resize(binary.size());
             if (!binary.empty()) {
             if (!binary.empty()) {
                 memmove(&opt_str[0], &binary[0], binary.size());
                 memmove(&opt_str[0], &binary[0], binary.size());
             }
             }
+        } else {
+            opt_str = "true";
         }
         }
+    } else if (representation_type_ == EXISTS) {
+        opt_str = "false";
     }
     }
 
 
     // Push value of the option or empty string if there was no such option
     // Push value of the option or empty string if there was no such option
@@ -190,3 +194,65 @@ OptionPtr TokenRelay4Option::getOption(const Pkt& pkt) {
     // If there is, try to return its suboption
     // If there is, try to return its suboption
     return (rai->getOption(option_code_));
     return (rai->getOption(option_code_));
 }
 }
+
+void
+TokenNot::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
+
+    if (values.size() == 0) {
+        isc_throw(EvalBadStack, "Incorrect empty stack.");
+    }
+
+    string op = values.top();
+    values.pop();
+    bool val = toBool(op);
+
+    if (!val) {
+	values.push("true");
+    } else {
+	values.push("false");
+    }
+}
+
+void
+TokenAnd::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
+
+    if (values.size() < 2) {
+        isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
+                  "2 values for and operator, got " << values.size());
+    }
+
+    string op1 = values.top();
+    values.pop();
+    bool val1 = toBool(op1);
+    string op2 = values.top();
+    values.pop(); // Dammit, std::stack interface is awkward.
+    bool val2 = toBool(op2);
+
+    if (val1 && val2) {
+	values.push("true");
+    } else {
+	values.push("false");
+    }
+}
+
+void
+TokenOr::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
+
+    if (values.size() < 2) {
+        isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
+                  "2 values for or operator, got " << values.size());
+    }
+
+    string op1 = values.top();
+    values.pop();
+    bool val1 = toBool(op1);
+    string op2 = values.top();
+    values.pop(); // Dammit, std::stack interface is awkward.
+    bool val2 = toBool(op2);
+
+    if (val1 || val2) {
+	values.push("true");
+    } else {
+	values.push("false");
+    }
+}

+ 97 - 3
src/lib/eval/token.h

@@ -79,6 +79,24 @@ public:
 
 
     /// @brief Virtual destructor
     /// @brief Virtual destructor
     virtual ~Token() {}
     virtual ~Token() {}
+
+    /// @brief Coverts a (string) value to a boolean
+    ///
+    /// Only "true" and "false" are expected.
+    ///
+    /// @param the (string) value
+    /// @return the boolean represented by the value
+    /// @throw EvalTypeError when the value is not either "true" or "false".
+    static inline bool toBool(std::string value) {
+        if (value == "true") {
+            return (true);
+        } else if (value == "false") {
+            return (false);
+        } else {
+            isc_throw(EvalTypeError, "Incorrect boolean. Expected exactly "
+                      "\"false\" or \"true\", got \"" << value << "\"");
+        }
+    }
 };
 };
 
 
 /// @brief Token representing a constant string
 /// @brief Token representing a constant string
@@ -136,20 +154,22 @@ protected:
 /// option[vendor-class].text
 /// option[vendor-class].text
 ///
 ///
 /// During the evaluation it tries to extract the value of the specified
 /// During the evaluation it tries to extract the value of the specified
-/// option. If the option is not found, an empty string ("") is returned.
+/// option. If the option is not found, an empty string ("") is returned
+/// (or "false" when the representation is EXISTS).
 class TokenOption : public Token {
 class TokenOption : public Token {
 public:
 public:
 
 
     /// @brief Token representation type.
     /// @brief Token representation type.
     ///
     ///
     /// There are many possible ways in which option can be presented.
     /// There are many possible ways in which option can be presented.
-    /// Currently the textual and hexadecimal representations are
+    /// Currently the textual, hexadecimal and exists representations are
     /// supported. The type of representation is specified in the
     /// supported. The type of representation is specified in the
     /// constructor and it affects the value generated by the
     /// constructor and it affects the value generated by the
     /// @c TokenOption::evaluate function.
     /// @c TokenOption::evaluate function.
     enum RepresentationType {
     enum RepresentationType {
         TEXTUAL,
         TEXTUAL,
-        HEXADECIMAL
+        HEXADECIMAL,
+        EXISTS
     };
     };
 
 
     /// @brief Constructor that takes an option code as a parameter
     /// @brief Constructor that takes an option code as a parameter
@@ -308,6 +328,80 @@ protected:
     virtual OptionPtr getOption(const Pkt& pkt);
     virtual OptionPtr getOption(const Pkt& pkt);
 };
 };
 
 
+/// @brief Token that represents logical negation operator
+///
+/// For example in the expression "not(option[vendor-class].text == 'MSF')"
+/// this token represents the leading "not"
+class TokenNot : public Token {
+public:
+    /// @brief Constructor (does nothing)
+    TokenNot() {}
+
+    /// @brief Logical negation.
+    ///
+    /// Evaluation does not use packet information, but rather consumes the last
+    /// result. It does a simple string comparison and sets the value to
+    /// either "true" or "false". It requires at least one value to be
+    /// present on stack and to be either "true" or "false".
+    ///
+    /// @throw EvalBadStack if there are less than 1 value on stack
+    /// @throw EvalTypeError if the top value on the stack is not either
+    ///        "true" or "false"
+    ///
+    /// @param pkt (unused)
+    /// @param values - stack of values (logical top value negated)
+    void evaluate(const Pkt& pkt, ValueStack& values);
+};
+
+/// @brief Token that represents logical and operator
+///
+/// For example "option[10].exists and option[11].exists"
+class TokenAnd : public Token {
+public:
+    /// @brief Constructor (does nothing)
+    TokenAnd() {}
+
+    /// @brief Logical and.
+    ///
+    /// Evaluation does not use packet information, but rather consumes the last
+    /// two parameters. It returns "true" if and only if both are "true".
+    /// It requires at least two logical (i.e., "true" or "false') values
+    /// present on stack.
+    ///
+    /// @throw EvalBadStack if there are less than 2 values on stack
+    /// @throw EvalTypeError if one of the 2 values on stack is not
+    ///        "true" or "false"
+    ///
+    /// @param pkt (unused)
+    /// @param values - stack of values (2 arguments will be popped, 1 result
+    ///        will be pushed)
+    void evaluate(const Pkt& pkt, ValueStack& values);
+};
+
+/// @brief Token that represents logical or operator
+///
+/// For example "option[10].exists or option[11].exists"
+class TokenOr : public Token {
+public:
+    /// @brief Constructor (does nothing)
+    TokenOr() {}
+
+    /// @brief Logical or.
+    ///
+    /// Evaluation does not use packet information, but rather consumes the last
+    /// two parameters. It returns "false" if and only if both are "false".
+    /// It requires at least two logical (i.e., "true" or "false') values
+    /// present on stack.
+    ///
+    /// @throw EvalBadStack if there are less than 2 values on stack
+    /// @throw EvalTypeError if one of the 2 values on stack is not
+    ///        "true" or "false"
+    ///
+    /// @param pkt (unused)
+    /// @param values - stack of values (2 arguments will be popped, 1 result
+    ///        will be pushed)
+    void evaluate(const Pkt& pkt, ValueStack& values);
+};
 
 
 }; // end of isc::dhcp namespace
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
 }; // end of isc namespace