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
 	Client classification in DHCPv4 has been enhanced. It is now
 	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 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
 sub-option</entry><entry>relay[code].hex</entry><entry>The value of
 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>
+      "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
         "code" from the option inserted as the Relay Agent Information
         (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>
           <tbody>
 <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>
           </tbody>
           </tgroup>
@@ -221,6 +230,15 @@ sub-option with code "code" from the Relay Agent Information option
       </para>
 
       <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>
         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
@@ -376,7 +394,7 @@ sub-option with code "code" from the Relay Agent Information option
       </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
         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
 // 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 "
                   "1 value at the end of evaluatuion, got " << values.size());
     }
-    if (values.top() == "false") {
-        return (false);
-    } else if (values.top() == "true") {
-        return (true);
-    } else {
-        isc_throw(EvalTypeError, "Incorrect evaluation type. Expected "
-                  "\"false\" or \"true\", got \"" << values.top() << "\"");
-    }
+    return (Token::toBool(values.top()));
 }
 
 }; // end of isc::dhcp namespace

+ 184 - 155
src/lib/eval/lexer.cc

@@ -18,7 +18,7 @@
 #define FLEX_SCANNER
 #define YY_FLEX_MAJOR_VERSION 2
 #define YY_FLEX_MINOR_VERSION 5
-#define YY_FLEX_SUBMINOR_VERSION 39
+#define YY_FLEX_SUBMINOR_VERSION 35
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #define FLEX_BETA
 #endif
@@ -72,6 +72,7 @@ typedef int16_t flex_int16_t;
 typedef uint16_t flex_uint16_t;
 typedef int32_t flex_int32_t;
 typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
 #else
 typedef signed char flex_int8_t;
 typedef short int flex_int16_t;
@@ -79,6 +80,7 @@ typedef int flex_int32_t;
 typedef unsigned char flex_uint8_t; 
 typedef unsigned short int flex_uint16_t;
 typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
 
 /* Limits of integral types. */
 #ifndef INT8_MIN
@@ -109,8 +111,6 @@ typedef unsigned int flex_uint32_t;
 #define UINT32_MAX             (4294967295U)
 #endif
 
-#endif /* ! C99 */
-
 #endif /* ! FLEXINT_H */
 
 /* %endif */
@@ -185,15 +185,7 @@ typedef unsigned int flex_uint32_t;
 
 /* Size of default input buffer. */
 #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
-#endif /* __ia64__ */
 #endif
 
 /* 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) \
             do { \
-                int yyl;\
+                yy_size_t yyl;\
                 for ( yyl = n; yyl < yyleng; ++yyl )\
                     if ( yytext[yyl] == '\n' )\
                         --yylineno;\
             }while(0)
-    #define YY_LINENO_REWIND_TO(dst) \
-            do {\
-                const char *p;\
-                for ( p = yy_cp-1; p >= (dst); --p)\
-                    if ( *p == '\n' )\
-                        --yylineno;\
-            }while(0)
     
 /* Return all but the first "n" matched characters back to the input stream. */
 #define yyless(n) \
@@ -435,7 +420,7 @@ void yyfree (void *  );
 /* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */
 /* Begin user sect3 */
 
-#define yywrap() 1
+#define yywrap(n) 1
 #define YY_SKIP_YYWRAP
 
 #define FLEX_DEBUG
@@ -453,8 +438,6 @@ int yylineno = 1;
 extern char *yytext;
 #define yytext_ptr yytext
 
-/* %% [1.5] DFA */
-
 /* %if-c-only Standard (non-C++) definition */
 
 static yy_state_type yy_get_previous_state (void );
@@ -470,15 +453,15 @@ static void yy_fatal_error (yyconst char msg[]  );
 #define YY_DO_BEFORE_ACTION \
 	(yytext_ptr) = yy_bp; \
 /* %% [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_cp = '\0'; \
 /* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\
 	(yy_c_buf_p) = yy_cp;
 
 /* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */
-#define YY_NUM_RULES 21
-#define YY_END_OF_BUFFER 22
+#define YY_NUM_RULES 25
+#define YY_END_OF_BUFFER 26
 /* This struct is not used in this scanner,
    but its presence is necessary. */
 struct yy_trans_info
@@ -486,30 +469,33 @@ struct yy_trans_info
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static yyconst flex_int16_t yy_acclist[99] =
+static yyconst flex_int16_t yy_acclist[119] =
     {   0,
-       22,   20,   21,    1,   20,   21,    2,   21,   20,   21,
-       15,   20,   21,   16,   20,   21,   19,   20,   21,   20,
-       21,   14,   20,   21,    5,   20,   21,    5,   20,   21,
-       20,   21,   20,   21,16390,   17,   20,   21,   18,   20,
-       21,   20,   21,16390,   20,   21,16390,   20,   21,16390,
-       20,   21,16390,   20,   21,16390,   20,   21,16390,    1,
-        2,    3,    5,    7,16390, 8198,16390,16390,16390,16390,
-    16390,16390,    4,   13,16390,   10,16390,16390,16390,16390,
-    16390,16390,16390,16390,    9,16390,16390,16390,16390,    8,
-    16390,   12,16390,16390,16390,16390,   11,16390
-
+       26,   24,   25,    1,   24,   25,    2,   25,   24,   25,
+       19,   24,   25,   20,   24,   25,   23,   24,   25,   24,
+       25,   18,   24,   25,    5,   24,   25,    5,   24,   25,
+       24,   25,   24,   25,16390,   21,   24,   25,   22,   24,
+       25,   24,   25,16390,   24,   25,16390,   24,   25,16390,
+       24,   25,16390,   24,   25,16390,   24,   25,16390,   24,
+       25,16390,   24,   25,16390,    1,    2,    3,    5,    7,
+    16390, 8198,16390,16390,16390,16390,16390,16390,   17,16390,
+    16390,16390,16390,    4,   14,16390,   16,16390,16390,   10,
+    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,
         1,    1,    1,    2,    4,    7,    9,   11,   14,   17,
        20,   22,   25,   28,   31,   33,   36,   39,   42,   45,
-       48,   51,   54,   57,   60,   61,   62,   62,   63,   64,
-       64,   65,   65,   65,   66,   67,   68,   69,   70,   71,
-       72,   73,   74,   76,   78,   79,   80,   81,   82,   83,
-       84,   85,   87,   88,   89,   90,   92,   94,   95,   96,
-       97,   99,   99
+       48,   51,   54,   57,   60,   63,   66,   67,   68,   68,
+       69,   70,   70,   71,   71,   71,   72,   73,   74,   75,
+       76,   77,   78,   79,   81,   82,   83,   84,   85,   87,
+       89,   90,   92,   94,   95,   96,   97,   98,   99,  100,
+      101,  102,  104,  105,  106,  107,  108,  110,  112,  114,
+      115,  116,  117,  119,  119
     } ;
 
 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,
        15,   15,   15,   15,   15,   15,   15,   15,   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,
-       28,   29,   15,   30,   31,   32,   33,   15,   15,   34,
-       35,   15,    1,    1,    1,    1,    1,    1,    1,    1,
+       23,   14,   24,   25,   26,   15,   15,   27,   15,   28,
+       29,   30,   15,   31,   32,   33,   34,   15,   15,   35,
+       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,
@@ -544,95 +530,114 @@ static yyconst flex_int32_t yy_ec[256] =
         1,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int32_t yy_meta[36] =
+static yyconst flex_int32_t yy_meta[37] =
     {   0,
         1,    2,    3,    1,    1,    1,    1,    2,    1,    4,
         4,    4,    1,    4,    2,    2,    1,    2,    2,    4,
-        4,    4,    2,    2,    2,    2,    2,    2,    2,    2,
-        2,    2,    2,    2,    2
+        4,    4,    4,    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,  135,  136,  132,  130,  128,  136,  136,  136,
-       26,  136,   29,   32,  118,   46,  136,  136,   64,   24,
-       26,   25,   27,   35,  128,  126,  124,  136,   58,    0,
-      136,   56,   73,  101,  136,  100,   45,   30,   99,   52,
-       51,    0,   98,   97,   55,   66,   58,   61,   68,   62,
-       69,   96,   76,   86,   74,   95,   90,   80,   81,   87,
-       85,  136,  113,  116,  105
+        0,    0,  113,  205,  108,  101,   98,  205,  205,  205,
+       27,  205,   30,   33,   87,   45,  205,  205,   64,   22,
+       28,   31,   43,   52,   34,   58,   82,   66,   50,  205,
+       66,    0,  205,   85,   87,   66,  205,   68,   79,   71,
+       81,   84,   91,   93,   95,  104,   99,    0,  101,  108,
+      110,  112,  116,  119,  121,  123,  125,  129,  132,  136,
+      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,
-       62,    1,   62,   62,   62,   62,   63,   62,   62,   62,
-       62,   62,   62,   62,   62,   64,   62,   62,   64,   19,
-       19,   19,   19,   19,   62,   62,   63,   62,   62,   65,
-       62,   62,   19,   19,   62,   19,   19,   19,   19,   19,
-       19,   65,   19,   19,   19,   19,   19,   19,   19,   19,
+       74,    1,   74,   74,   74,   74,   75,   74,   74,   74,
+       74,   74,   74,   74,   74,   76,   74,   74,   76,   19,
+       19,   19,   19,   19,   19,   19,   74,   74,   75,   74,
+       74,   77,   74,   74,   19,   19,   74,   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,    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,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
        14,   14,   15,   16,   16,   16,   17,   18,    4,   19,
-       16,   16,   16,   20,   16,   16,   16,   21,   16,   22,
-       23,   24,   16,   16,   16,   29,   29,   29,   29,   29,
-       29,   29,   29,   29,   30,   37,   39,   32,   32,   34,
-       34,   34,   34,   33,   38,   34,   41,   32,   32,   40,
-       34,   45,   30,   35,   33,   32,   32,   29,   29,   29,
-       34,   33,   47,   35,   62,   62,   34,   34,   44,   49,
-       34,   35,   33,   34,   48,   50,   34,   34,   51,   36,
-       62,   34,   52,   34,   34,   53,   54,   57,   34,   34,
-
-       55,   34,   56,   58,   59,   34,   34,   60,   42,   61,
-       34,   34,   34,   27,   27,   34,   27,   34,   34,   34,
-       34,   34,   34,   34,   46,   43,   34,   28,   26,   25,
-       31,   28,   26,   25,   62,    3,   62,   62,   62,   62,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
-       62
+       16,   16,   20,   16,   21,   16,   16,   22,   23,   16,
+       24,   25,   26,   16,   16,   16,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   32,   34,   34,   36,   36,
+       41,   48,   35,   30,   36,   36,   40,   36,   36,   42,
+       36,   36,   37,   35,   32,   34,   34,   46,   28,   36,
+       36,   35,   43,   44,   45,   31,   31,   31,   36,   36,
+       47,   37,   35,   27,   36,   36,   34,   34,   74,   74,
+       38,   39,   36,   36,   49,   36,   51,   36,   36,   33,
+
+       50,   30,   37,   28,   74,   36,   36,   36,   36,   27,
+       36,   36,   74,   36,   36,   52,   53,   36,   36,   36,
+       36,   55,   36,   54,   56,   36,   36,   36,   36,   74,
+       36,   36,   74,   57,   36,   36,   36,   36,   36,   36,
+       60,   58,   36,   36,   59,   36,   36,   36,   36,   36,
+       36,   36,   36,   74,   61,   36,   36,   62,   36,   36,
+       64,   63,   36,   36,   36,   36,   36,   36,   36,   36,
+       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,
         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,
-       13,   14,   14,   14,   13,   20,   22,   16,   16,   20,
-       22,   21,   23,   16,   21,   38,   24,   32,   32,   23,
-       24,   38,   13,   16,   16,   19,   19,   29,   29,   29,
-       37,   19,   40,   32,   33,   33,   41,   40,   37,   45,
-       45,   19,   19,   47,   41,   46,   48,   50,   47,   19,
-       33,   46,   48,   49,   51,   49,   50,   54,   33,   55,
-
-       51,   53,   53,   55,   58,   58,   59,   59,   65,   60,
-       61,   54,   60,   63,   63,   57,   63,   64,   64,   64,
-       56,   52,   44,   43,   39,   36,   34,   27,   26,   25,
-       15,    7,    6,    5,    3,   62,   62,   62,   62,   62,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
-       62,   62,   62,   62,   62,   62,   62,   62,   62,   62,
-       62
+        1,    1,    1,    1,    1,    1,   11,   11,   11,   13,
+       13,   13,   14,   14,   14,   13,   16,   16,   20,   20,
+       21,   77,   16,   29,   21,   21,   20,   22,   22,   22,
+       25,   25,   16,   16,   13,   19,   19,   25,   28,   23,
+       23,   19,   23,   23,   24,   31,   31,   31,   24,   24,
+       26,   19,   19,   27,   26,   26,   34,   34,   35,   35,
+       19,   19,   36,   36,   38,   38,   40,   40,   40,   15,
+
+       39,    7,   34,    6,   35,   39,   39,   41,   41,    5,
+       42,   42,    3,   35,   35,   41,   42,   43,   43,   44,
+       44,   45,   45,   43,   46,   47,   47,   49,   49,    0,
+       46,   46,    0,   47,   50,   50,   51,   51,   52,   52,
+       55,   51,   53,   53,   54,   54,   54,   55,   55,   56,
+       56,   57,   57,    0,   56,   58,   58,   57,   59,   59,
+       59,   58,   60,   60,   61,   61,   62,   62,   63,   63,
+       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. */
-static yyconst flex_int32_t yy_rule_can_match_eol[22] =
+static yyconst flex_int32_t yy_rule_can_match_eol[26] =
     {   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;
 int yy_flex_debug = 1;
 
-static yyconst flex_int16_t yy_rule_linenum[21] =
+static yyconst flex_int16_t yy_rule_linenum[25] =
     {   0,
        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;
@@ -709,7 +714,7 @@ static isc::eval::location loc;
 // by moving it ahead by yyleng bytes. yyleng specifies the length of the
 // currently matched token.
 #define YY_USER_ACTION  loc.columns(yyleng);
-#line 713 "lexer.cc"
+#line 718 "lexer.cc"
 
 #define INITIAL 0
 
@@ -818,12 +823,7 @@ static int input (void );
 
 /* Amount of stuff to slurp up with each read. */
 #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
-#endif /* __ia64__ */
 #endif
 
 /* 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,
  * we now use fwrite().
  */
-#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
 /* %endif */
 /* %if-c++-only C++ definition */
 /* %endif */
@@ -847,7 +847,7 @@ static int input (void );
 	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
 		{ \
 		int c = '*'; \
-		size_t n; \
+		yy_size_t n; \
 		for ( n = 0; n < max_size && \
 			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
 			buf[n] = (char) c; \
@@ -953,6 +953,17 @@ YY_DECL
 	register char *yy_cp, *yy_bp;
 	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) )
 		{
 		(yy_init) = 1;
@@ -993,18 +1004,6 @@ YY_DECL
 		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 */
 		{
 /* %% [8.0] yymore()-related code goes here */
@@ -1027,23 +1026,24 @@ YY_DECL
 yy_match:
 		do
 			{
-			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
-				if ( yy_current_state >= 63 )
+				if ( yy_current_state >= 75 )
 					yy_c = yy_meta[(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_cp;
 			}
-		while ( yy_current_state != 62 );
+		while ( yy_current_state != 74 );
 
 yy_find_action:
 /* %% [10.0] code to find the action number goes here */
 		yy_current_state = *--(yy_state_ptr);
 		(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 */
 		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 )
 				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",
 				         (long)yy_rule_linenum[yy_act], yytext );
-			else if ( yy_act == 21 )
+			else if ( yy_act == 25 )
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				         yytext );
-			else if ( yy_act == 22 )
+			else if ( yy_act == 26 )
 				fprintf( stderr, "--(end of buffer or a NUL)\n" );
 			else
 				fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
@@ -1207,63 +1207,83 @@ return isc::eval::EvalParser::make_HEX(loc);
 case 11:
 YY_RULE_SETUP
 #line 129 "lexer.ll"
-return isc::eval::EvalParser::make_SUBSTRING(loc);
+return isc::eval::EvalParser::make_EXISTS(loc);
 	YY_BREAK
 case 12:
 YY_RULE_SETUP
 #line 130 "lexer.ll"
-return isc::eval::EvalParser::make_RELAY4(loc);
+return isc::eval::EvalParser::make_SUBSTRING(loc);
 	YY_BREAK
 case 13:
 YY_RULE_SETUP
 #line 131 "lexer.ll"
-return isc::eval::EvalParser::make_ALL(loc);
+return isc::eval::EvalParser::make_RELAY4(loc);
 	YY_BREAK
 case 14:
 YY_RULE_SETUP
 #line 132 "lexer.ll"
-return isc::eval::EvalParser::make_DOT(loc);
+return isc::eval::EvalParser::make_ALL(loc);
 	YY_BREAK
 case 15:
 YY_RULE_SETUP
 #line 133 "lexer.ll"
-return isc::eval::EvalParser::make_LPAREN(loc);
+return isc::eval::EvalParser::make_NOT(loc);
 	YY_BREAK
 case 16:
 YY_RULE_SETUP
 #line 134 "lexer.ll"
-return isc::eval::EvalParser::make_RPAREN(loc);
+return isc::eval::EvalParser::make_AND(loc);
 	YY_BREAK
 case 17:
 YY_RULE_SETUP
 #line 135 "lexer.ll"
-return isc::eval::EvalParser::make_LBRACKET(loc);
+return isc::eval::EvalParser::make_OR(loc);
 	YY_BREAK
 case 18:
 YY_RULE_SETUP
 #line 136 "lexer.ll"
-return isc::eval::EvalParser::make_RBRACKET(loc);
+return isc::eval::EvalParser::make_DOT(loc);
 	YY_BREAK
 case 19:
 YY_RULE_SETUP
 #line 137 "lexer.ll"
-return isc::eval::EvalParser::make_COMA(loc);
+return isc::eval::EvalParser::make_LPAREN(loc);
 	YY_BREAK
 case 20:
 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"
+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));
 	YY_BREAK
 case YY_STATE_EOF(INITIAL):
-#line 140 "lexer.ll"
+#line 144 "lexer.ll"
 return isc::eval::EvalParser::make_END(loc);
 	YY_BREAK
-case 21:
+case 25:
 YY_RULE_SETUP
-#line 141 "lexer.ll"
+#line 145 "lexer.ll"
 ECHO;
 	YY_BREAK
-#line 1267 "lexer.cc"
+#line 1287 "lexer.cc"
 
 	case YY_END_OF_BUFFER:
 		{
@@ -1393,7 +1413,6 @@ ECHO;
 			"fatal flex scanner internal error--no action found" );
 	} /* end of action switch */
 		} /* end of scanning one token */
-	} /* end of user's declarations */
 } /* end of yylex */
 /* %ok-for-header */
 
@@ -1544,7 +1563,7 @@ static int yy_get_next_buffer (void)
 		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 			{
 			yy_current_state = (int) yy_def[yy_current_state];
-			if ( yy_current_state >= 63 )
+			if ( yy_current_state >= 75 )
 				yy_c = yy_meta[(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 )
 		{
 		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_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 )
 		*(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 */
@@ -1639,7 +1658,7 @@ static int yy_get_next_buffer (void)
 				case EOB_ACT_END_OF_FILE:
 					{
 					if ( yywrap( ) )
-						return EOF;
+						return 0;
 
 					if ( ! (yy_did_buffer_switch_on_eof) )
 						YY_NEW_FILE;
@@ -1803,6 +1822,17 @@ static void yy_load_buffer_state  (void)
 	yyfree((void *) b  );
 }
 
+/* %if-c-only */
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* %endif */
+
+/* %if-c++-only */
+/* %endif */
+
 /* Initializes or reinitializes a buffer.
  * This function is sometimes called more than once on the same buffer,
  * such as during a yyrestart() or at EOF.
@@ -2043,8 +2073,8 @@ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
 /* %if-c-only */
 /** Setup the input buffer state to scan the given bytes. The next call to yylex() will
  * scan from a @e copy of @a bytes.
- * @param yybytes the byte buffer to scan
- * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
  * 
  * @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;
 	char *buf;
-	yy_size_t n;
-	yy_size_t i;
+	yy_size_t n, i;
     
 	/* Get memory for full buffer, including space for trailing EOB's. */
 	n = _yybytes_len + 2;
@@ -2324,7 +2353,7 @@ void yyfree (void * ptr )
 
 /* %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());
     if (!buffer) {
         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);
 "text"      return isc::eval::EvalParser::make_TEXT(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);
 "relay4"    return isc::eval::EvalParser::make_RELAY4(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_LPAREN(loc);
 ")"         return isc::eval::EvalParser::make_RPAREN(loc);
@@ -151,7 +155,7 @@ EvalContext::scanStringBegin()
     buffer = yy_scan_bytes(string_.c_str(), string_.size());
     if (!buffer) {
         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++
 

+ 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++
 
-// 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
 // it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@
 
 // First part of user declarations.
 
-#line 37 "parser.cc" // lalr1.cc:399
+#line 37 "parser.cc" // lalr1.cc:404
 
 # ifndef YY_NULLPTR
 #  if defined __cplusplus && 201103L <= __cplusplus
@@ -47,13 +47,13 @@
 
 // User implementation prologue.
 
-#line 51 "parser.cc" // lalr1.cc:407
+#line 51 "parser.cc" // lalr1.cc:412
 // Unqualified %code blocks.
-#line 32 "parser.yy" // lalr1.cc:408
+#line 32 "parser.yy" // lalr1.cc:413
 
 # include "eval_context.h"
 
-#line 57 "parser.cc" // lalr1.cc:408
+#line 57 "parser.cc" // lalr1.cc:413
 
 
 #ifndef YY_
@@ -130,16 +130,16 @@
 #endif // !YYDEBUG
 
 #define yyerrok         (yyerrstatus_ = 0)
-#define yyclearin       (yyempty = true)
+#define yyclearin       (yyla.clear ())
 
 #define YYACCEPT        goto yyacceptlab
 #define YYABORT         goto yyabortlab
 #define YYERROR         goto yyerrorlab
 #define YYRECOVERING()  (!!yyerrstatus_)
 
-#line 13 "parser.yy" // lalr1.cc:474
+#line 13 "parser.yy" // lalr1.cc:479
 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
      backslashes, so that it's suitable for yyerror.  The heuristic is
@@ -202,7 +202,7 @@ namespace isc { namespace eval {
   // by_state.
   inline
   EvalParser::by_state::by_state ()
-    : state (empty)
+    : state (empty_state)
   {}
 
   inline
@@ -212,10 +212,17 @@ namespace isc { namespace eval {
 
   inline
   void
+  EvalParser::by_state::clear ()
+  {
+    state = empty_state;
+  }
+
+  inline
+  void
   EvalParser::by_state::move (by_state& that)
   {
     state = that.state;
-    that.state = empty;
+    that.clear ();
   }
 
   inline
@@ -227,7 +234,10 @@ namespace isc { namespace eval {
   EvalParser::symbol_number_type
   EvalParser::by_state::type_get () const
   {
-    return state == empty ? 0 : yystos_[state];
+    if (state == empty_state)
+      return empty_symbol;
+    else
+      return yystos_[state];
   }
 
   inline
@@ -241,19 +251,19 @@ namespace isc { namespace eval {
   {
       switch (that.type_get ())
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.move< TokenOption::RepresentationType > (that.value);
         break;
 
-      case 16: // "constant string"
-      case 17: // "integer"
-      case 18: // "constant hexstring"
-      case 19: // "option name"
-      case 20: // TOKEN
+      case 20: // "constant string"
+      case 21: // "integer"
+      case 22: // "constant hexstring"
+      case 23: // "option name"
+      case 24: // TOKEN
         value.move< std::string > (that.value);
         break;
 
-      case 25: // option_code
+      case 29: // option_code
         value.move< uint16_t > (that.value);
         break;
 
@@ -262,7 +272,7 @@ namespace isc { namespace eval {
     }
 
     // that is emptied.
-    that.type = empty;
+    that.type = empty_symbol;
   }
 
   inline
@@ -272,19 +282,19 @@ namespace isc { namespace eval {
     state = that.state;
       switch (that.type_get ())
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.copy< TokenOption::RepresentationType > (that.value);
         break;
 
-      case 16: // "constant string"
-      case 17: // "integer"
-      case 18: // "constant hexstring"
-      case 19: // "option name"
-      case 20: // TOKEN
+      case 20: // "constant string"
+      case 21: // "integer"
+      case 22: // "constant hexstring"
+      case 23: // "option name"
+      case 24: // TOKEN
         value.copy< std::string > (that.value);
         break;
 
-      case 25: // option_code
+      case 29: // option_code
         value.copy< uint16_t > (that.value);
         break;
 
@@ -315,58 +325,62 @@ namespace isc { namespace eval {
     std::ostream& yyoutput = yyo;
     YYUSE (yyoutput);
     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")
         << ' ' << yytname_[yytype] << " ("
         << yysym.location << ": ";
     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 > (); }
-#line 328 "parser.cc" // lalr1.cc:617
+#line 342 "parser.cc" // lalr1.cc:636
         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 > (); }
-#line 335 "parser.cc" // lalr1.cc:617
+#line 349 "parser.cc" // lalr1.cc:636
         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 > (); }
-#line 342 "parser.cc" // lalr1.cc:617
+#line 356 "parser.cc" // lalr1.cc:636
         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 > (); }
-#line 349 "parser.cc" // lalr1.cc:617
+#line 363 "parser.cc" // lalr1.cc:636
         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 > (); }
-#line 356 "parser.cc" // lalr1.cc:617
+#line 370 "parser.cc" // lalr1.cc:636
         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 > (); }
-#line 363 "parser.cc" // lalr1.cc:617
+#line 377 "parser.cc" // lalr1.cc:636
         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 > (); }
-#line 370 "parser.cc" // lalr1.cc:617
+#line 384 "parser.cc" // lalr1.cc:636
         break;
 
 
@@ -453,9 +467,6 @@ namespace isc { namespace eval {
   int
   EvalParser::parse ()
   {
-    /// Whether yyla contains a lookahead.
-    bool yyempty = true;
-
     // State.
     int yyn;
     /// Length of the RHS of the rule being reduced.
@@ -507,7 +518,7 @@ namespace isc { namespace eval {
       goto yydefault;
 
     // Read a lookahead token.
-    if (yyempty)
+    if (yyla.empty ())
       {
         YYCDEBUG << "Reading a token: ";
         try
@@ -520,7 +531,6 @@ namespace isc { namespace eval {
             error (yyexc);
             goto yyerrlab1;
           }
-        yyempty = false;
       }
     YY_SYMBOL_PRINT ("Next token is", yyla);
 
@@ -540,9 +550,6 @@ namespace isc { namespace eval {
         goto yyreduce;
       }
 
-    // Discard the token being shifted.
-    yyempty = true;
-
     // Count tokens shifted since error; after three, turn off error status.
     if (yyerrstatus_)
       --yyerrstatus_;
@@ -573,19 +580,19 @@ namespace isc { namespace eval {
          when using variants.  */
         switch (yyr1_[yyn])
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         yylhs.value.build< TokenOption::RepresentationType > ();
         break;
 
-      case 16: // "constant string"
-      case 17: // "integer"
-      case 18: // "constant hexstring"
-      case 19: // "option name"
-      case 20: // TOKEN
+      case 20: // "constant string"
+      case 21: // "integer"
+      case 22: // "constant hexstring"
+      case 23: // "option name"
+      case 24: // TOKEN
         yylhs.value.build< std::string > ();
         break;
 
-      case 25: // option_code
+      case 29: // option_code
         yylhs.value.build< uint16_t > ();
         break;
 
@@ -606,44 +613,80 @@ namespace isc { namespace eval {
         {
           switch (yyn)
             {
-  case 3:
-#line 76 "parser.yy" // lalr1.cc:847
+  case 4:
+#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());
                     ctx.expression.push_back(eq);
                 }
-#line 616 "parser.cc" // lalr1.cc:847
+#line 650 "parser.cc" // lalr1.cc:859
     break;
 
-  case 4:
-#line 83 "parser.yy" // lalr1.cc:847
+  case 8:
+#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 > ()));
                       ctx.expression.push_back(str);
                   }
-#line 625 "parser.cc" // lalr1.cc:847
+#line 668 "parser.cc" // lalr1.cc:859
     break;
 
-  case 5:
-#line 88 "parser.yy" // lalr1.cc:847
+  case 10:
+#line 117 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(hex);
                   }
-#line 634 "parser.cc" // lalr1.cc:847
+#line 677 "parser.cc" // lalr1.cc:859
     break;
 
-  case 6:
-#line 93 "parser.yy" // lalr1.cc:847
+  case 11:
+#line 122 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), yystack_[0].value.as< TokenOption::RepresentationType > ()));
                       ctx.expression.push_back(opt);
                   }
-#line 643 "parser.cc" // lalr1.cc:847
+#line 686 "parser.cc" // lalr1.cc:859
     break;
 
-  case 7:
-#line 98 "parser.yy" // lalr1.cc:847
+  case 12:
+#line 127 "parser.yy" // lalr1.cc:859
     {
                      switch (ctx.getUniverse()) {
                      case Option::V4:
@@ -663,79 +706,79 @@ namespace isc { namespace eval {
                          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;
 
-  case 8:
-#line 118 "parser.yy" // lalr1.cc:847
+  case 13:
+#line 147 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr sub(new TokenSubstring());
                       ctx.expression.push_back(sub);
                   }
-#line 676 "parser.cc" // lalr1.cc:847
+#line 719 "parser.cc" // lalr1.cc:859
     break;
 
-  case 10:
-#line 127 "parser.yy" // lalr1.cc:847
+  case 15:
+#line 156 "parser.yy" // lalr1.cc:859
     {
                      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;
 
-  case 11:
-#line 131 "parser.yy" // lalr1.cc:847
+  case 16:
+#line 160 "parser.yy" // lalr1.cc:859
     {
                      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;
 
-  case 12:
-#line 137 "parser.yy" // lalr1.cc:847
+  case 17:
+#line 166 "parser.yy" // lalr1.cc:859
     {
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
                       }
-#line 700 "parser.cc" // lalr1.cc:847
+#line 743 "parser.cc" // lalr1.cc:859
     break;
 
-  case 13:
-#line 141 "parser.yy" // lalr1.cc:847
+  case 18:
+#line 170 "parser.yy" // lalr1.cc:859
     {
                           yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
                       }
-#line 708 "parser.cc" // lalr1.cc:847
+#line 751 "parser.cc" // lalr1.cc:859
     break;
 
-  case 14:
-#line 147 "parser.yy" // lalr1.cc:847
+  case 19:
+#line 176 "parser.yy" // lalr1.cc:859
     {
                      TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                      ctx.expression.push_back(str);
                  }
-#line 717 "parser.cc" // lalr1.cc:847
+#line 760 "parser.cc" // lalr1.cc:859
     break;
 
-  case 15:
-#line 154 "parser.yy" // lalr1.cc:847
+  case 20:
+#line 183 "parser.yy" // lalr1.cc:859
     {
                       TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
                       ctx.expression.push_back(str);
                   }
-#line 726 "parser.cc" // lalr1.cc:847
+#line 769 "parser.cc" // lalr1.cc:859
     break;
 
-  case 16:
-#line 159 "parser.yy" // lalr1.cc:847
+  case 21:
+#line 188 "parser.yy" // lalr1.cc:859
     {
                      TokenPtr str(new TokenString("all"));
                      ctx.expression.push_back(str);
                  }
-#line 735 "parser.cc" // lalr1.cc:847
+#line 778 "parser.cc" // lalr1.cc:859
     break;
 
 
-#line 739 "parser.cc" // lalr1.cc:847
+#line 782 "parser.cc" // lalr1.cc:859
             default:
               break;
             }
@@ -763,8 +806,7 @@ namespace isc { namespace eval {
     if (!yyerrstatus_)
       {
         ++yynerrs_;
-        error (yyla.location, yysyntax_error_ (yystack_[0].state,
-                                           yyempty ? yyempty_ : yyla.type_get ()));
+        error (yyla.location, yysyntax_error_ (yystack_[0].state, yyla));
       }
 
 
@@ -777,10 +819,10 @@ namespace isc { namespace eval {
         // Return failure if at end of input.
         if (yyla.type_get () == yyeof_)
           YYABORT;
-        else if (!yyempty)
+        else if (!yyla.empty ())
           {
             yy_destroy_ ("Error: discarding", yyla);
-            yyempty = true;
+            yyla.clear ();
           }
       }
 
@@ -856,7 +898,7 @@ namespace isc { namespace eval {
     goto yyreturn;
 
   yyreturn:
-    if (!yyempty)
+    if (!yyla.empty ())
       yy_destroy_ ("Cleanup: discarding lookahead", yyla);
 
     /* Do not reclaim the symbols of the rule whose action triggered
@@ -876,7 +918,7 @@ namespace isc { namespace eval {
                  << std::endl;
         // Do not try to display the values of the reclaimed symbols,
         // as their printer might throw an exception.
-        if (!yyempty)
+        if (!yyla.empty ())
           yy_destroy_ (YY_NULLPTR, yyla);
 
         while (1 < yystack_.size ())
@@ -896,9 +938,8 @@ namespace isc { namespace eval {
 
   // Generate an error message.
   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
     // "expected").
     size_t yycount = 0;
@@ -912,7 +953,7 @@ namespace isc { namespace eval {
          the only way this function was invoked is if the default action
          is an error action.  In that case, don't check for expected
          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.
          Thus, detecting the absence of a lookahead is sufficient 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
          later state.
     */
-    if (yytoken != yyempty_)
+    if (!yyla.empty ())
       {
+        int yytoken = yyla.type_get ();
         yyarg[yycount++] = yytname_[yytoken];
         int yyn = yypact_[yystate];
         if (!yy_pact_value_is_default_ (yyn))
@@ -976,6 +1018,7 @@ namespace isc { namespace eval {
 #undef YYCASE_
       }
 
+    std::string yyres;
     // Argument number.
     size_t yyi = 0;
     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::yypact_[] =
   {
-      -4,    -3,     3,    -1,   -14,   -14,   -14,    17,   -14,    15,
-     -13,    -4,   -13,   -14,    -4,   -14,   -14,     4,     9,     6,
-     -14,    12,     7,    13,     1,   -14,    14,     1,   -14,   -14,
-     -14,    -7,   -14,   -14,   -14,    16,   -14
+      -4,    -8,     9,    -4,    12,    -4,   -11,   -11,   -11,    29,
+       7,    37,    16,    -1,   -11,    16,     0,   -11,    -4,    -4,
+      -1,   -11,   -11,    23,    25,    26,    28,   -11,   -11,    38,
+     -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
   EvalParser::yydefact_[] =
   {
-       0,     0,     0,     0,     4,     5,     9,     0,     2,     0,
-       0,     0,     0,     1,     0,    10,    11,     0,     0,     0,
-       3,     0,     0,     0,     0,    14,     0,     0,    12,    13,
-       6,     0,     7,    16,    15,     0,     8
+       0,     0,     0,     0,     0,     0,     9,    10,    14,     0,
+       2,     0,     0,     0,     4,     0,     0,     1,     0,     0,
+       0,    15,    16,     0,     0,     0,     0,     3,     5,     6,
+       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
   EvalParser::yypgoto_[] =
   {
-     -14,   -14,   -14,    -6,    18,     0,   -14,   -14
+     -11,   -11,     8,    15,   -10,    18,   -11,   -11
   };
 
   const signed char
   EvalParser::yydefgoto_[] =
   {
-      -1,     7,     8,     9,    17,    30,    26,    35
+      -1,     9,    10,    11,    23,    43,    38,    50
   };
 
   const unsigned char
   EvalParser::yytable_[] =
   {
-       1,     2,    33,     3,    15,    18,    16,    28,    20,    29,
-      34,    10,     4,    12,     5,    11,     6,    13,    14,    21,
-      22,    23,    24,    27,    25,    31,     0,    32,     0,    36,
-      19
+       1,     2,     3,    24,     2,    26,     4,    18,    19,     4,
+      12,    14,     5,    16,    18,    19,     6,    27,     7,     6,
+       8,     7,    36,     8,    48,    13,    28,    29,    25,    17,
+      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
   EvalParser::yycheck_[] =
   {
-       4,     5,     9,     7,    17,    11,    19,     6,    14,     8,
-      17,    14,    16,    14,    18,    12,    20,     0,     3,    15,
-      11,    15,    10,    10,    17,    11,    -1,    27,    -1,    13,
-      12
+       4,     5,     6,     4,     5,    15,    10,     7,     8,    10,
+      18,     3,    16,     5,     7,     8,    20,    17,    22,    20,
+      24,    22,    32,    24,    13,    16,    18,    19,    13,     0,
+      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
   EvalParser::yystos_[] =
   {
-       0,     4,     5,     7,    16,    18,    20,    22,    23,    24,
-      14,    12,    14,     0,     3,    17,    19,    25,    24,    25,
-      24,    15,    11,    15,    10,    17,    27,    10,     6,     8,
-      26,    11,    26,     9,    17,    28,    13
+       0,     4,     5,     6,    10,    16,    20,    22,    24,    26,
+      27,    28,    18,    16,    27,    18,    27,     0,     7,     8,
+       3,    21,    23,    29,     4,    28,    29,    17,    27,    27,
+      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
   EvalParser::yyr1_[] =
   {
-       0,    21,    22,    23,    24,    24,    24,    24,    24,    24,
-      25,    25,    26,    26,    27,    28,    28
+       0,    25,    26,    27,    27,    27,    27,    27,    27,    28,
+      28,    28,    28,    28,    28,    29,    29,    30,    30,    31,
+      32,    32
   };
 
   const unsigned char
   EvalParser::yyr2_[] =
   {
-       0,     2,     1,     3,     1,     1,     6,     6,     8,     1,
-       1,     1,     1,     1,     1,     1,     1
+       0,     2,     1,     3,     2,     3,     3,     3,     6,     1,
+       1,     6,     6,     8,     1,     1,     1,     1,     1,     1,
+       1,     1
   };
 
 
@@ -1073,10 +1128,11 @@ namespace isc { namespace eval {
   const EvalParser::yytname_[] =
   {
   "\"end of file\"", "error", "$undefined", "\"==\"", "\"option\"",
-  "\"substring\"", "\"text\"", "\"relay4\"", "\"hex\"", "\"all\"", "\".\"",
-  "\",\"", "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"",
-  "\"integer\"", "\"constant hexstring\"", "\"option name\"", "TOKEN",
-  "$accept", "expression", "bool_expr", "string_expr", "option_code",
+  "\"substring\"", "\"not\"", "\"and\"", "\"or\"", "\"text\"",
+  "\"relay4\"", "\"hex\"", "\"exists\"", "\"all\"", "\".\"", "\",\"",
+  "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"", "\"integer\"",
+  "\"constant hexstring\"", "\"option name\"", "TOKEN", "$accept",
+  "expression", "bool_expr", "string_expr", "option_code",
   "option_repr_type", "start_expr", "length_expr", YY_NULLPTR
   };
 
@@ -1084,8 +1140,9 @@ namespace isc { namespace eval {
   const unsigned char
   EvalParser::yyrline_[] =
   {
-       0,    72,    72,    75,    82,    87,    92,    97,   117,   122,
-     126,   130,   136,   140,   146,   153,   158
+       0,    80,    80,    83,    84,    89,    94,    99,   104,   111,
+     116,   121,   126,   146,   151,   155,   159,   165,   169,   175,
+     182,   187
   };
 
   // Print the state stack on the debug stream.
@@ -1118,10 +1175,10 @@ namespace isc { namespace eval {
 #endif // YYDEBUG
 
 
-#line 13 "parser.yy" // lalr1.cc:1155
+#line 13 "parser.yy" // lalr1.cc:1167
 } } // isc::eval
-#line 1124 "parser.cc" // lalr1.cc:1155
-#line 165 "parser.yy" // lalr1.cc:1156
+#line 1181 "parser.cc" // lalr1.cc:1167
+#line 194 "parser.yy" // lalr1.cc:1168
 
 void
 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++
 
-// 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
 // it under the terms of the GNU General Public License as published by
@@ -40,7 +40,7 @@
 #ifndef YY_YY_PARSER_H_INCLUDED
 # define YY_YY_PARSER_H_INCLUDED
 // //                    "%code requires" blocks.
-#line 16 "parser.yy" // lalr1.cc:372
+#line 16 "parser.yy" // lalr1.cc:392
 
 #include <string>
 #include <eval/token.h>
@@ -51,13 +51,14 @@
 using namespace isc::dhcp;
 using namespace isc::eval;
 
-#line 55 "parser.h" // lalr1.cc:372
+#line 55 "parser.h" // lalr1.cc:392
 
 # include <cassert>
-# include <vector>
+# include <cstdlib> // std::abort
 # include <iostream>
 # include <stdexcept>
 # include <string>
+# include <vector>
 # include "stack.hh"
 # include "location.hh"
 #include <typeinfo>
@@ -125,9 +126,9 @@ using namespace isc::eval;
 # define YYDEBUG 1
 #endif
 
-#line 13 "parser.yy" // lalr1.cc:372
+#line 13 "parser.yy" // lalr1.cc:392
 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.
     variant ()
-      : yytname_ (YY_NULLPTR)
+      : yytypeid_ (YY_NULLPTR)
     {}
 
     /// Construct and fill.
     template <typename T>
     variant (const T& t)
-      : yytname_ (typeid (T).name ())
+      : yytypeid_ (&typeid (T))
     {
       YYASSERT (sizeof (T) <= S);
       new (yyas_<T> ()) T (t);
@@ -159,7 +160,7 @@ namespace isc { namespace eval {
     /// Destruction, allowed only if empty.
     ~variant ()
     {
-      YYASSERT (!yytname_);
+      YYASSERT (!yytypeid_);
     }
 
     /// Instantiate an empty \a T in here.
@@ -167,9 +168,9 @@ namespace isc { namespace eval {
     T&
     build ()
     {
-      YYASSERT (!yytname_);
+      YYASSERT (!yytypeid_);
       YYASSERT (sizeof (T) <= S);
-      yytname_ = typeid (T).name ();
+      yytypeid_ = & typeid (T);
       return *new (yyas_<T> ()) T;
     }
 
@@ -178,9 +179,9 @@ namespace isc { namespace eval {
     T&
     build (const T& t)
     {
-      YYASSERT (!yytname_);
+      YYASSERT (!yytypeid_);
       YYASSERT (sizeof (T) <= S);
-      yytname_ = typeid (T).name ();
+      yytypeid_ = & typeid (T);
       return *new (yyas_<T> ()) T (t);
     }
 
@@ -189,7 +190,7 @@ namespace isc { namespace eval {
     T&
     as ()
     {
-      YYASSERT (yytname_ == typeid (T).name ());
+      YYASSERT (*yytypeid_ == typeid (T));
       YYASSERT (sizeof (T) <= S);
       return *yyas_<T> ();
     }
@@ -199,7 +200,7 @@ namespace isc { namespace eval {
     const T&
     as () const
     {
-      YYASSERT (yytname_ == typeid (T).name ());
+      YYASSERT (*yytypeid_ == typeid (T));
       YYASSERT (sizeof (T) <= S);
       return *yyas_<T> ();
     }
@@ -216,8 +217,8 @@ namespace isc { namespace eval {
     void
     swap (self_type& other)
     {
-      YYASSERT (yytname_);
-      YYASSERT (yytname_ == other.yytname_);
+      YYASSERT (yytypeid_);
+      YYASSERT (*yytypeid_ == *other.yytypeid_);
       std::swap (as<T> (), other.as<T> ());
     }
 
@@ -247,7 +248,7 @@ namespace isc { namespace eval {
     destroy ()
     {
       as<T> ().~T ();
-      yytname_ = YY_NULLPTR;
+      yytypeid_ = YY_NULLPTR;
     }
 
   private:
@@ -282,7 +283,7 @@ namespace isc { namespace eval {
     } yybuffer_;
 
     /// 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_OPTION = 259,
         TOKEN_SUBSTRING = 260,
-        TOKEN_TEXT = 261,
-        TOKEN_RELAY4 = 262,
-        TOKEN_HEX = 263,
-        TOKEN_ALL = 264,
-        TOKEN_DOT = 265,
-        TOKEN_COMA = 266,
-        TOKEN_LPAREN = 267,
-        TOKEN_RPAREN = 268,
-        TOKEN_LBRACKET = 269,
-        TOKEN_RBRACKET = 270,
-        TOKEN_STRING = 271,
-        TOKEN_INTEGER = 272,
-        TOKEN_HEXSTRING = 273,
-        TOKEN_OPTION_NAME = 274,
-        TOKEN_TOKEN = 275
+        TOKEN_NOT = 261,
+        TOKEN_AND = 262,
+        TOKEN_OR = 263,
+        TOKEN_TEXT = 264,
+        TOKEN_RELAY4 = 265,
+        TOKEN_HEX = 266,
+        TOKEN_EXISTS = 267,
+        TOKEN_ALL = 268,
+        TOKEN_DOT = 269,
+        TOKEN_COMA = 270,
+        TOKEN_LPAREN = 271,
+        TOKEN_RPAREN = 272,
+        TOKEN_LBRACKET = 273,
+        TOKEN_RBRACKET = 274,
+        TOKEN_STRING = 275,
+        TOKEN_INTEGER = 276,
+        TOKEN_HEXSTRING = 277,
+        TOKEN_OPTION_NAME = 278,
+        TOKEN_TOKEN = 279
       };
     };
 
     /// (External) token type, as returned by yylex.
     typedef token::yytokentype token_type;
 
-    /// Internal symbol number.
+    /// Symbol type: an internal symbol number.
     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).
     typedef unsigned char token_number_type;
 
@@ -393,8 +401,15 @@ namespace isc { namespace eval {
                     const semantic_type& v,
                     const location_type& l);
 
+      /// Destroy the 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.
       void move (basic_symbol& s);
 
@@ -424,21 +439,23 @@ namespace isc { namespace eval {
       /// Constructor from (external) token numbers.
       by_type (kind_type t);
 
+      /// Record that this symbol is empty.
+      void clear ();
+
       /// Steal the symbol type from \a that.
       void move (by_type& that);
 
       /// The (internal) type number (corresponding to \a type).
-      /// -1 when this symbol is empty.
+      /// \a empty when empty.
       symbol_number_type type_get () const;
 
       /// The token.
       token_type token () const;
 
-      enum { empty = 0 };
-
       /// The symbol type.
-      /// -1 when this symbol is empty.
-      token_number_type type;
+      /// \a empty_symbol when empty.
+      /// An int, not token_number_type, to be able to store empty_symbol.
+      int type;
     };
 
     /// "External" symbols: returned by the scanner.
@@ -463,6 +480,18 @@ namespace isc { namespace eval {
 
     static inline
     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);
 
     static inline
@@ -475,6 +504,10 @@ namespace isc { namespace eval {
 
     static inline
     symbol_type
+    make_EXISTS (const location_type& l);
+
+    static inline
+    symbol_type
     make_ALL (const location_type& l);
 
     static inline
@@ -562,9 +595,9 @@ namespace isc { namespace eval {
 
     /// Generate an error message.
     /// \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,
-                                         symbol_number_type yytoken) const;
+                                         const symbol_type& yyla) const;
 
     /// Compute post-reduction state.
     /// \param yystate   the current state
@@ -667,16 +700,21 @@ namespace isc { namespace eval {
       /// Copy constructor.
       by_state (const by_state& other);
 
+      /// Record that this symbol is empty.
+      void clear ();
+
       /// Steal the symbol type from \a that.
       void move (by_state& that);
 
       /// The (internal) type number (corresponding to \a state).
-      /// "empty" when empty.
+      /// \a empty_symbol when empty.
       symbol_number_type type_get () const;
 
-      enum { empty = 0 };
+      /// The state number used to denote an empty symbol.
+      enum { empty_state = -1 };
 
       /// The state.
+      /// \a empty when empty.
       state_type state;
     };
 
@@ -717,17 +755,16 @@ namespace isc { namespace eval {
     /// Pop \a n symbols the three stacks.
     void yypop_ (unsigned int n = 1);
 
-    // Constants.
+    /// Constants.
     enum
     {
       yyeof_ = 0,
-      yylast_ = 30,     ///< Last index in yytable_.
+      yylast_ = 57,     ///< Last index in yytable_.
       yynnts_ = 8,  ///< Number of nonterminal symbols.
-      yyempty_ = -2,
-      yyfinal_ = 13, ///< Termination state number.
+      yyfinal_ = 17, ///< Termination state number.
       yyterror_ = 1,
       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,     1,     2,     3,     4,
        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;
 
     if (static_cast<int>(t) <= yyeof_)
@@ -806,19 +843,19 @@ namespace isc { namespace eval {
   {
       switch (other.type_get ())
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.copy< TokenOption::RepresentationType > (other.value);
         break;
 
-      case 16: // "constant string"
-      case 17: // "integer"
-      case 18: // "constant hexstring"
-      case 19: // "option name"
-      case 20: // TOKEN
+      case 20: // "constant string"
+      case 21: // "integer"
+      case 22: // "constant hexstring"
+      case 23: // "option name"
+      case 24: // TOKEN
         value.copy< std::string > (other.value);
         break;
 
-      case 25: // option_code
+      case 29: // option_code
         value.copy< uint16_t > (other.value);
         break;
 
@@ -839,19 +876,19 @@ namespace isc { namespace eval {
     (void) v;
       switch (this->type_get ())
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.copy< TokenOption::RepresentationType > (v);
         break;
 
-      case 16: // "constant string"
-      case 17: // "integer"
-      case 18: // "constant hexstring"
-      case 19: // "option name"
-      case 20: // TOKEN
+      case 20: // "constant string"
+      case 21: // "integer"
+      case 22: // "constant hexstring"
+      case 23: // "option name"
+      case 24: // TOKEN
         value.copy< std::string > (v);
         break;
 
-      case 25: // option_code
+      case 29: // option_code
         value.copy< uint16_t > (v);
         break;
 
@@ -896,8 +933,18 @@ namespace isc { namespace eval {
   inline
   EvalParser::basic_symbol<Base>::~basic_symbol ()
   {
+    clear ();
+  }
+
+  template <typename Base>
+  inline
+  void
+  EvalParser::basic_symbol<Base>::clear ()
+  {
     // User destructor.
     symbol_number_type yytype = this->type_get ();
+    basic_symbol<Base>& yysym = *this;
+    (void) yysym;
     switch (yytype)
     {
    default:
@@ -907,19 +954,19 @@ namespace isc { namespace eval {
     // Type destructor.
     switch (yytype)
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.template destroy< TokenOption::RepresentationType > ();
         break;
 
-      case 16: // "constant string"
-      case 17: // "integer"
-      case 18: // "constant hexstring"
-      case 19: // "option name"
-      case 20: // TOKEN
+      case 20: // "constant string"
+      case 21: // "integer"
+      case 22: // "constant hexstring"
+      case 23: // "option name"
+      case 24: // TOKEN
         value.template destroy< std::string > ();
         break;
 
-      case 25: // option_code
+      case 29: // option_code
         value.template destroy< uint16_t > ();
         break;
 
@@ -927,6 +974,15 @@ namespace isc { namespace eval {
         break;
     }
 
+    Base::clear ();
+  }
+
+  template <typename Base>
+  inline
+  bool
+  EvalParser::basic_symbol<Base>::empty () const
+  {
+    return Base::type_get () == empty_symbol;
   }
 
   template <typename Base>
@@ -937,19 +993,19 @@ namespace isc { namespace eval {
     super_type::move(s);
       switch (this->type_get ())
     {
-      case 26: // option_repr_type
+      case 30: // option_repr_type
         value.move< TokenOption::RepresentationType > (s.value);
         break;
 
-      case 16: // "constant string"
-      case 17: // "integer"
-      case 18: // "constant hexstring"
-      case 19: // "option name"
-      case 20: // TOKEN
+      case 20: // "constant string"
+      case 21: // "integer"
+      case 22: // "constant hexstring"
+      case 23: // "option name"
+      case 24: // TOKEN
         value.move< std::string > (s.value);
         break;
 
-      case 25: // option_code
+      case 29: // option_code
         value.move< uint16_t > (s.value);
         break;
 
@@ -963,7 +1019,7 @@ namespace isc { namespace eval {
   // by_type.
   inline
   EvalParser::by_type::by_type ()
-     : type (empty)
+    : type (empty_symbol)
   {}
 
   inline
@@ -978,10 +1034,17 @@ namespace isc { namespace eval {
 
   inline
   void
+  EvalParser::by_type::clear ()
+  {
+    type = empty_symbol;
+  }
+
+  inline
+  void
   EvalParser::by_type::move (by_type& that)
   {
     type = that.type;
-    that.type = empty;
+    that.clear ();
   }
 
   inline
@@ -1003,7 +1066,7 @@ namespace isc { namespace eval {
     {
        0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
      265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
-     275
+     275,   276,   277,   278,   279
     };
     return static_cast<token_type> (yytoken_number_[type]);
   }
@@ -1033,6 +1096,24 @@ namespace isc { namespace eval {
   }
 
   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)
   {
     return symbol_type (token::TOKEN_TEXT, l);
@@ -1051,6 +1132,12 @@ namespace isc { namespace eval {
   }
 
   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)
   {
     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
-#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 "=="
   OPTION "option"
   SUBSTRING "substring"
+  NOT "not"
+  AND "and"
+  OR "or"
   TEXT "text"
   RELAY4 "relay4"
   HEX "hex"
+  EXISTS "exists"
   ALL "all"
   DOT "."
   COMA ","
@@ -60,6 +64,10 @@ using namespace isc::eval;
 %type <uint16_t> option_code
 %type <TokenOption::RepresentationType> option_repr_type
 
+%left OR
+%left AND
+%precedence NOT
+
 %printer { yyoutput << $$; } <*>;
 
 %%
@@ -72,11 +80,32 @@ using namespace isc::eval;
 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());
                     ctx.expression.push_back(eq);
                 }
+          | OPTION "[" option_code "]" "." EXISTS
+                {
+                    TokenPtr opt(new TokenOption($3, TokenOption::EXISTS));
+                    ctx.expression.push_back(opt);
+                }
           ;
 
 string_expr : STRING
@@ -120,7 +149,7 @@ string_expr : STRING
                       ctx.expression.push_back(sub);
                   }
             | TOKEN
-                // Temporary unused token to avoid explict but long errors
+                // Temporary unused token to avoid explicit but long errors
             ;
 
 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++
 

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

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

@@ -26,7 +26,8 @@ if HAVE_GTEST
 
 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 += token_unittest.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);
 }
 
+// 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_F(EvalContextTest, optionWithNameAndWhitespace) {
     EvalContext eval(Option::V4);
@@ -265,6 +275,112 @@ TEST_F(EvalContextTest, optionHex) {
     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_F(EvalContextTest, substring) {
     EvalContext eval(Option::V4);
@@ -369,9 +485,45 @@ TEST_F(EvalContextTest, parseErrors) {
     checkError("'foo''bar'",
                "<string>:1.6-10: syntax error, unexpected constant string, "
                "expecting ==");
+    checkError("'foo' (",
+               "<string>:1.7: syntax error, unexpected (, expecting ==");
     checkError("== 'ab'", "<string>:1.1-2: syntax error, unexpected ==");
     checkError("'foo' ==",
                "<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'",
                "<string>:1.8-11: syntax error, unexpected "
                "constant string, expecting [");
@@ -390,6 +542,9 @@ TEST_F(EvalContextTest, parseErrors) {
                "expecting integer or option name");
     checkError("option[10].bin", "<string>:1.12: 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'",
                "<string>:1.19: syntax error, "
                "unexpected ), expecting \",\"");
@@ -402,7 +557,7 @@ TEST_F(EvalContextTest, parseErrors) {
                "<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) {
     checkError("'foobar'",
                "<string>:1.9: syntax error, unexpected end of file, "
@@ -413,6 +568,16 @@ TEST_F(EvalContextTest, typeErrors) {
     checkError("substring('foobar',0x32,1) == 'foo'",
                "<string>:1.20-23: syntax error, unexpected constant "
                "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
 // 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_);
 }
 
+// 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.
 TEST_F(EvaluateTest, packet) {
     TokenPtr toption;

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

@@ -125,6 +125,24 @@ public:
     /// @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,
 // can be used in Pkt4 evaluation. (The actual packet is not used)
 TEST_F(TokenTest, string4) {
@@ -322,6 +340,28 @@ TEST_F(TokenTest, optionHexString4) {
     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
 // the option from an IPv6 packet and properly store the option's value.
 TEST_F(TokenTest, optionString6) {
@@ -379,6 +419,28 @@ TEST_F(TokenTest, optionHexString6) {
     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
 // compare two values (with incorrectly built stack).
 TEST_F(TokenTest, optionEqualInvalid) {
@@ -426,6 +488,171 @@ TEST_F(TokenTest, optionEqualTrue) {
     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

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

@@ -66,13 +66,17 @@ TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
     if (opt) {
         if (representation_type_ == TEXTUAL) {
             opt_str = opt->toString();
-        } else {
+        } else if (representation_type_ == HEXADECIMAL) {
             std::vector<uint8_t> binary = opt->toBinary();
             opt_str.resize(binary.size());
             if (!binary.empty()) {
                 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
@@ -190,3 +194,65 @@ OptionPtr TokenRelay4Option::getOption(const Pkt& pkt) {
     // If there is, try to return its suboption
     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
     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
@@ -136,20 +154,22 @@ protected:
 /// option[vendor-class].text
 ///
 /// 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 {
 public:
 
     /// @brief Token representation type.
     ///
     /// 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
     /// constructor and it affects the value generated by the
     /// @c TokenOption::evaluate function.
     enum RepresentationType {
         TEXTUAL,
-        HEXADECIMAL
+        HEXADECIMAL,
+        EXISTS
     };
 
     /// @brief Constructor that takes an option code as a parameter
@@ -308,6 +328,80 @@ protected:
     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 namespace