Browse Source

[master] Merged trac5363 (ifelse)

Francis Dupont 7 years ago
parent
commit
9c56281461

+ 10 - 0
doc/guide/classify.xml

@@ -579,6 +579,7 @@
 <row><entry>Substring</entry><entry>substring('foobar',0,3)</entry><entry>Return the requested substring</entry></row>
 <row><entry>Concat</entry><entry>concat('foo','bar')</entry><entry>Return the
 concatenation of the strings</entry></row>
+<row><entry>Ifelse</entry><entry>ifelse('foo' == 'bar','us','them')</entry><entry>Return the branch value according to the condition</entry></row>
           </tbody>
           </tgroup>
         </table>
@@ -624,6 +625,15 @@ concatenation of the strings</entry></row>
         concat('foo', 'bar') == 'foobar'
           </screen>
        </section>
+       <section>
+         <title>Ifelse</title>
+         The ifelse function "ifelse(cond, iftrue, ifelse)" returns the
+         "iftrue" or "ifelse" branch value following the boolean
+         condition "cond". For instance:
+           <screen>
+         ifelse(option[230].exists, option[230].hex, 'none')
+           </screen>
+        </section>
     </section>
 
   <note>

+ 6 - 5
src/lib/eval/eval.dox

@@ -132,13 +132,13 @@ instantiated with the appropriate value and put onto the expression vector.
  isc::eval::Token class and represents a certain expression primitive.
  Currently supported tokens are:
 
- - isc::dhcp::TokenString -- represents a constant string, e.g. "MSFT";
+ - isc::dhcp::TokenString -- represents a constant string, e.g. "MSFT".
  - isc::dhcp::TokenHexString -- represents a constant string, encoded as
-   hex string, e.g. 0x666f6f which is actually "foo";
+   hex string, e.g. 0x666f6f which is actually "foo".
  - isc::dhcp::TokenIpAddress -- represents a constant IP address, encoded as
    a 4 or 16 byte binary string, e.g., 10.0.0.1 is 0x10000001.
  - isc::dhcp::TokenOption -- represents an option in a packet, e.g.
-                    option[123].text;
+                    option[123].text.
  - isc::dhcp::TokenRelay4Option -- represents a sub-option inserted by the
                     DHCPv4 relay, e.g. relay[123].text or relay[123].hex
  - isc::dhcp::TokenRelay6Option -- represents a sub-option inserted by
@@ -149,10 +149,11 @@ instantiated with the appropriate value and put onto the expression vector.
  - isc::dhcp::TokenPkt6 -- represents a DHCPv6 packet field (message type
    or transaction id).
  - isc::dhcp::TokenRelay6Field -- represents a DHCPv6 relay information field.
- - isc::dhcp::TokenEqual -- represents the equal (==) operator;
- - isc::dhcp::TokenSubstring -- represents the substring(text, start, length) operator;
+ - isc::dhcp::TokenEqual -- represents the equal (==) operator.
+ - isc::dhcp::TokenSubstring -- represents the substring(text, start, length) operator.
  - isc::dhcp::TokenConcat -- represents the concat operator which
    concatenate two other tokens.
+ - isc::dhcp::TokenIfElse == represents the ifelse(cond, iftrue, ifelse) operator.
  - isc::dhcp::TokenNot -- the logical not operator.
  - isc::dhcp::TokenAnd -- the logical and (strict) operator.
  - isc::dhcp::TokenOr -- the logical or (strict) operator (strict means

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

@@ -34,6 +34,18 @@ the value stack.  The strings are displayed in hex.
 This debug message indicates that the given binary string is being pushed
 onto the value stack.  The string is displayed in hex.
 
+# For use with TokenIfElse
+
+% EVAL_DEBUG_IFELSE_FALSE Popping %1 (false) and %2, leaving %3
+This debug message indicates that the condition is false so
+the iftrue branch value is removed and the ifelse branch value
+is left on the value stack.
+
+% EVAL_DEBUG_IFELSE_TRUE Popping %1 (true) and %2, leaving %3
+This debug message indicates that the condition is true so
+the ifelse branch value is removed and the iftrue branch value
+is left on the value stack.
+
 # For use with TokenIpAddress
 
 % EVAL_DEBUG_IPADDRESS Pushing IPAddress %1

+ 174 - 166
src/lib/eval/lexer.cc

@@ -710,8 +710,8 @@ static void yynoreturn yy_fatal_error ( const char* msg  );
 /* %% [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 51
-#define YY_END_OF_BUFFER 52
+#define YY_NUM_RULES 52
+#define YY_END_OF_BUFFER 53
 /* This struct is not used in this scanner,
    but its presence is necessary. */
 struct yy_trans_info
@@ -719,41 +719,42 @@ struct yy_trans_info
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static const flex_int16_t yy_acclist[280] =
+static const flex_int16_t yy_acclist[285] =
     {   0,
-       52,   50,   51,    1,   50,   51,    2,   51,   50,   51,
-       44,   50,   51,   45,   50,   51,   49,   50,   51,   48,
-       50,   51,   50,   51,   43,   50,   51,    5,   50,   51,
-        5,   50,   51,   50,   51,   50,   51,   50,   51,16390,
-       50,   51,16390,   46,   50,   51,   47,   50,   51,   50,
-       51,16390,   50,   51,16390,   50,   51,16390,   50,   51,
-    16390,   50,   51,16390,   50,   51,16390,   50,   51,16390,
-       50,   51,16390,   50,   51,16390,   50,   51,16390,   50,
-       51,16390,   50,   51,16390,   50,   51,16390,   50,   51,
-    16390,   50,   51,16390,   50,   51,16390,   50,   51,16390,
+       53,   51,   52,    1,   51,   52,    2,   52,   51,   52,
+       45,   51,   52,   46,   51,   52,   50,   51,   52,   49,
+       51,   52,   51,   52,   44,   51,   52,    5,   51,   52,
+        5,   51,   52,   51,   52,   51,   52,   51,   52,16390,
+       51,   52,16390,   47,   51,   52,   48,   51,   52,   51,
+       52,16390,   51,   52,16390,   51,   52,16390,   51,   52,
+    16390,   51,   52,16390,   51,   52,16390,   51,   52,16390,
+       51,   52,16390,   51,   52,16390,   51,   52,16390,   51,
+       52,16390,   51,   52,16390,   51,   52,16390,   51,   52,
+    16390,   51,   52,16390,   51,   52,16390,   51,   52,16390,
 
         1,    2,    3,    5,    5,    7,    8,16390,16390, 8198,
     16390,16390,16390,16390,16390,16390,16390,16390,16390,16390,
-    16390,16390,16390,16390,16390,16390,16390,16390,16390,   42,
+    16390,16390,16390,16390,16390,16390,16390,16390,16390,   43,
     16390,16390,16390,16390,16390,16390,16390,16390,16390,16390,
-    16390,    4,    7,   38,16390,   41,16390,16390,16390,16390,
+    16390,    4,    7,   38,16390,   42,16390,16390,16390,16390,
        20,16390,16390,16390,16390,   15,16390,16390,16390,16390,
-       21,16390,16390,   23,16390,16390,   40,16390,16390,16390,
-       17,16390,16390,16390,   19,16390,16390,16390,16390,16390,
-    16390,16390,16390,   35,16390,16390,16390,16390,   24,16390,
-    16390,16390,16390,16390,16390,16390,   22,16390,   30,16390,
-
-    16390,16390,16390,   14,16390,16390,16390,16390,16390,16390,
-    16390,16390,16390,   25,16390,   18,16390,16390,16390,16390,
-    16390,16390,16390,16390,16390,16390,16390,   26,16390,   39,
-    16390,16390,   16,16390,   27,16390,16390,16390,    9,16390,
-    16390,   10,16390,   11,16390,   29,16390,16390,16390,   33,
-    16390,   28,16390,    7,16390,16390,   31,16390,16390,16390,
-       32,16390,16390,   13,16390,   12,16390,16390,16390,16390,
-       37,16390,16390,   36,16390,16390,16390,   34,16390
+    16390,   21,16390,16390,   23,16390,16390,   41,16390,16390,
+    16390,   17,16390,16390,16390,   19,16390,16390,16390,16390,
+    16390,16390,16390,16390,   35,16390,16390,16390,16390,   24,
+    16390,16390,16390,16390,16390,16390,16390,16390,   22,16390,
+
+       30,16390,16390,16390,16390,   14,16390,16390,16390,16390,
+    16390,16390,16390,16390,16390,   25,16390,   18,16390,16390,
+    16390,16390,16390,16390,16390,16390,16390,16390,16390,16390,
+       26,16390,   39,16390,16390,   16,16390,   27,16390,   40,
+    16390,16390,16390,    9,16390,16390,   10,16390,   11,16390,
+       29,16390,16390,16390,   33,16390,   28,16390,    7,16390,
+    16390,   31,16390,16390,16390,   32,16390,16390,   13,16390,
+       12,16390,16390,16390,16390,   37,16390,16390,   36,16390,
+    16390,16390,   34,16390
     } ;
 
-static const flex_int16_t yy_accept[199] =
+static const flex_int16_t yy_accept[203] =
     {   0,
         1,    1,    1,    2,    4,    7,    9,   11,   14,   17,
        20,   23,   25,   28,   31,   34,   36,   38,   41,   44,
@@ -766,17 +767,18 @@ static const flex_int16_t yy_accept[199] =
       136,  137,  138,  139,  140,  141,  142,  142,  143,  144,
       146,  148,  149,  150,  151,  153,  154,  155,  156,  158,
 
-      159,  160,  161,  163,  164,  166,  167,  169,  170,  171,
-      173,  174,  175,  177,  178,  179,  180,  181,  182,  182,
-      183,  184,  186,  187,  188,  189,  191,  192,  193,  194,
-      195,  196,  197,  199,  201,  202,  203,  204,  206,  207,
-      208,  209,  209,  210,  211,  212,  213,  214,  216,  218,
-      219,  220,  221,  222,  223,  224,  225,  226,  227,  228,
-      228,  230,  232,  233,  235,  237,  238,  239,  241,  242,
-      244,  246,  248,  249,  250,  252,  254,  255,  256,  257,
-      259,  260,  261,  263,  263,  264,  266,  268,  269,  270,
-      271,  273,  274,  276,  277,  278,  280,  280
-
+      159,  160,  161,  162,  164,  165,  167,  168,  170,  171,
+      172,  174,  175,  176,  178,  179,  180,  181,  182,  183,
+      183,  184,  185,  187,  188,  189,  190,  192,  193,  194,
+      195,  196,  197,  198,  199,  201,  203,  204,  205,  206,
+      208,  209,  210,  211,  211,  212,  213,  214,  215,  216,
+      218,  220,  221,  222,  223,  224,  225,  226,  227,  228,
+      229,  230,  231,  231,  233,  235,  236,  238,  240,  242,
+      243,  244,  246,  247,  249,  251,  253,  254,  255,  257,
+      259,  260,  261,  262,  264,  265,  266,  268,  268,  269,
+      271,  273,  274,  275,  276,  278,  279,  281,  282,  283,
+
+      285,  285
     } ;
 
 static const YY_CHAR yy_ec[256] =
@@ -820,104 +822,104 @@ static const YY_CHAR yy_meta[45] =
         1,    1,    1,    1
     } ;
 
-static const flex_int16_t yy_base[203] =
+static const flex_int16_t yy_base[207] =
     {   0,
-        0,    0,  310,  311,  307,  305,  303,  311,  311,  311,
-      311,   34,  311,   39,   36,  291,  289,   81,  115,  311,
-      311,   24,   37,   37,   26,  273,   45,  275,   43,   48,
-      266,   43,   59,  274,  106,   50,  273,  268,  296,  294,
-      292,  311,  122,  137,  112,  280,  279,    0,  278,    0,
-      311,  143,  150,    0,    0,  311,  259,  265,  267,  254,
-      248,  247,  246,  254,  261,  240,  255,  237,  257,  244,
-      243,  252,  247,  235,  234,    0,  246,  232,  238,  247,
-      244,  244,  224,  243,  230,  241,  146,    0,    0,    0,
-        0,  237,  237,  238,    0,  233,  220,  232,    0,  222,
-
-      219,  230,    0,  222,    0,  213,    0,  221,  213,  148,
-      227,  223,    0,  209,  207,  211,  219,  218,  154,  217,
-      219,    0,  203,  200,  213,    0,  211,  210,  213,  191,
-      198,  210,    0,    0,  188,  205,  190,    0,  190,  192,
-      201,  162,  188,  185,  187,  184,  184,    0,    0,  195,
-      173,  172,  180,  156,  167,  165,  171,  163,  162,  166,
-        0,    0,  161,    0,    0,  172,  170,    0,  170,    0,
-        0,    0,  164,  168,  184,    0,  170,  161,  153,    0,
-      152,  154,    0,  183,  149,    0,    0,  158,  130,  127,
-        0,   78,    0,   58,   50,    0,  311,  208,  210,  212,
-
-       71,  215
+        0,    0,  314,  315,  311,  309,  307,  315,  315,  315,
+      315,   34,  315,   39,   36,  295,  293,   81,  115,  315,
+      315,   24,   37,   37,   26,  277,   45,  279,   43,   48,
+      270,   43,   59,  278,  106,   50,  277,  272,  300,  298,
+      296,  315,  122,  137,  112,  284,  283,    0,  282,    0,
+      315,  143,  150,    0,    0,  315,  263,  269,  271,  258,
+      252,  251,  250,  258,  265,  244,  259,  241,   74,  249,
+      248,  257,  252,  240,  239,    0,  251,  237,  243,  252,
+      249,  249,  229,  248,  235,  246,  146,    0,    0,    0,
+        0,  242,  242,  243,    0,  238,  225,  237,    0,  227,
+
+      224,  235,  226,    0,  226,    0,  217,    0,  225,  217,
+      148,  231,  227,    0,  213,  211,  215,  223,  222,  154,
+      221,  223,    0,  207,  204,  217,    0,  215,  214,  201,
+      216,  194,  201,  213,    0,    0,  191,  208,  193,    0,
+      193,  195,  204,  162,  191,  188,  190,  187,  187,    0,
+        0,  197,  197,  185,  186,  184,  156,  169,  168,  174,
+      165,  164,  166,    0,    0,  163,    0,    0,    0,  174,
+      172,    0,  172,    0,    0,    0,  166,  170,  186,    0,
+      170,  163,  155,    0,  154,  156,    0,  183,  151,    0,
+        0,  160,  155,  160,    0,  140,    0,  115,   50,    0,
+
+      315,  208,  210,  212,   71,  215
     } ;
 
-static const flex_int16_t yy_def[203] =
+static const flex_int16_t yy_def[207] =
     {   0,
-      197,    1,  197,  197,  197,  197,  198,  197,  197,  197,
-      197,  197,  197,  197,   14,  199,  197,  197,   18,  197,
-      197,   18,   18,   18,   18,   19,   19,   19,   19,   19,
-       19,   19,   19,   19,   19,   19,   19,   19,  197,  197,
-      198,  197,  197,  197,   14,  199,  200,  201,  199,  202,
-      197,  197,   19,   18,   19,  197,   19,   19,   19,   19,
+      201,    1,  201,  201,  201,  201,  202,  201,  201,  201,
+      201,  201,  201,  201,   14,  203,  201,  201,   18,  201,
+      201,   18,   18,   18,   18,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,  201,  201,
+      202,  201,  201,  201,   14,  203,  204,  205,  203,  206,
+      201,  201,   19,   18,   19,  201,   19,   19,   19,   19,
        18,   19,   19,   19,   19,   19,   19,   19,   19,   19,
        19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
-       19,   19,   19,   19,   19,   19,  197,  201,  202,   19,
+       19,   19,   19,   19,   19,   19,  201,  205,  206,   19,
        19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
 
        19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
-       19,   19,   19,   19,   19,   19,   19,   19,  197,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,   19,  201,
        19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
        19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
-       19,  197,   19,   19,   19,   19,   19,   19,   19,   19,
-       19,   19,   19,   19,   19,   19,   19,   19,   19,  197,
+       19,   19,   19,  201,   19,   19,   19,   19,   19,   19,
        19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
-       19,   19,   19,   19,   19,   19,  197,   19,   19,   19,
+       19,   19,  201,   19,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
+      201,   19,   19,   19,   19,   19,   19,   19,   19,   19,
        19,   19,   19,   19,   19,   19,   19,   19,   19,   19,
-       19,   19,   19,   19,   19,   19,    0,  197,  197,  197,
 
-      197,  197
+        0,  201,  201,  201,  201,  201
     } ;
 
-static const flex_int16_t yy_nxt[356] =
+static const flex_int16_t yy_nxt[360] =
     {   0,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
        14,   15,   15,   15,   16,   17,   18,   19,   19,   20,
        21,    4,   22,   18,   23,   24,   25,   18,   26,   27,
        28,   19,   29,   30,   31,   32,   33,   34,   35,   36,
        19,   37,   19,   38,   43,   43,   43,   43,   44,   45,
-       45,   45,   45,   46,  197,   47,   57,   48,   58,   61,
+       45,   45,   45,   46,  201,   47,   57,   48,   58,   61,
        63,   47,   47,   47,   47,   47,   47,   59,   64,   70,
-       72,   66,   60,   71,   88,   62,   83,   67,  197,   75,
-       76,   48,   52,   52,   68,   77,   73,   84,  196,   53,
-       78,   54,   54,   54,   54,   46,  195,   54,   55,   55,
+       72,   66,   60,   71,   88,   62,   83,   67,  201,   75,
+       76,   48,   52,   52,   68,   77,   73,   84,  200,   53,
+       78,   54,   54,   54,   54,   46,  102,   54,   55,   55,
 
-      194,   56,   53,   54,   54,   54,   54,   54,   54,   55,
+      103,   56,   53,   54,   54,   54,   54,   54,   54,   55,
        55,   55,   55,   55,   55,   55,   55,   55,   55,   55,
-       55,   55,   55,   55,   55,   55,   55,   55,   55,  197,
-      197,   55,   43,   43,   43,   43,   80,   55,   55,   55,
+       55,   55,   55,   55,   55,   55,   55,   55,   55,  201,
+      201,   55,   43,   43,   43,   43,   80,   55,   55,   55,
        55,   55,   55,   81,   52,   52,   82,   87,   87,   87,
-       87,  197,  197,  193,  197,  119,   87,   87,   87,   87,
-      133,  134,  192,   56,  142,  142,  142,  142,  170,  171,
-      197,  160,  142,  142,  142,  142,  177,  177,  177,  177,
-      177,  177,  177,  177,  197,  197,  191,  190,  188,  187,
-      186,  185,  184,  183,  182,  181,  180,  179,  178,  176,
+       87,  201,  201,  199,  201,  120,   87,   87,   87,   87,
+      135,  136,  198,   56,  144,  144,  144,  144,  174,  175,
+      201,  163,  144,  144,  144,  144,  181,  181,  181,  181,
+      181,  181,  181,  181,  201,  201,  197,  196,  195,  194,
+      192,  191,  190,  189,  188,  187,  186,  185,  184,  183,
 
-      175,  174,  173,  197,  172,  169,  168,  189,   41,  167,
+      182,  180,  179,  201,  178,  177,  176,  193,   41,  173,
        41,   41,   41,   49,   49,   47,   47,   89,   89,   89,
-      166,  165,  164,  163,  162,  161,  159,  158,  157,  156,
-      155,  154,  153,  152,  151,  150,  149,  148,  147,  146,
-      145,  144,  143,  141,  140,  139,  138,  137,  136,  135,
-      132,  131,  130,  129,  128,  127,  126,  125,  124,  123,
-      122,  121,  120,  118,  117,  116,  115,  114,  113,  112,
-      111,  110,  109,  108,  107,  106,  105,  104,  103,  102,
-      101,  100,   99,   98,   97,   96,   95,   94,   93,   92,
-       91,   90,   50,   46,   50,   42,   40,   39,   86,   85,
-
-       79,   74,   69,   65,   51,   50,   42,   40,   39,  197,
-        3,  197,  197,  197,  197,  197,  197,  197,  197,  197,
-      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
-      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
-      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
-      197,  197,  197,  197,  197
+      172,  171,  170,  169,  168,  167,  166,  165,  164,  162,
+      161,  160,  159,  158,  157,  156,  155,  154,  153,  152,
+      151,  150,  149,  148,  147,  146,  145,  143,  142,  141,
+      140,  139,  138,  137,  134,  133,  132,  131,  130,  129,
+      128,  127,  126,  125,  124,  123,  122,  121,  119,  118,
+      117,  116,  115,  114,  113,  112,  111,  110,  109,  108,
+      107,  106,  105,  104,  101,  100,   99,   98,   97,   96,
+       95,   94,   93,   92,   91,   90,   50,   46,   50,   42,
+
+       40,   39,   86,   85,   79,   74,   69,   65,   51,   50,
+       42,   40,   39,  201,    3,  201,  201,  201,  201,  201,
+      201,  201,  201,  201,  201,  201,  201,  201,  201,  201,
+      201,  201,  201,  201,  201,  201,  201,  201,  201,  201,
+      201,  201,  201,  201,  201,  201,  201,  201,  201,  201,
+      201,  201,  201,  201,  201,  201,  201,  201,  201
     } ;
 
-static const flex_int16_t yy_chk[356] =
+static const flex_int16_t yy_chk[360] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -926,57 +928,58 @@ static const flex_int16_t yy_chk[356] =
         1,    1,    1,    1,   12,   12,   12,   12,   14,   14,
        14,   14,   14,   14,   15,   14,   22,   14,   22,   24,
        25,   14,   14,   14,   14,   14,   14,   23,   25,   29,
-       30,   27,   23,   29,  201,   24,   36,   27,   15,   32,
-       32,   14,   18,   18,   27,   33,   30,   36,  195,   18,
-       33,   18,   18,   18,   18,   18,  194,   18,   18,   18,
+       30,   27,   23,   29,  205,   24,   36,   27,   15,   32,
+       32,   14,   18,   18,   27,   33,   30,   36,  199,   18,
+       33,   18,   18,   18,   18,   18,   69,   18,   18,   18,
 
-      192,   18,   18,   18,   18,   18,   18,   18,   18,   18,
+       69,   18,   18,   18,   18,   18,   18,   18,   18,   18,
        18,   18,   18,   18,   18,   18,   18,   18,   18,   18,
        18,   18,   18,   18,   18,   19,   19,   19,   19,   19,
        45,   19,   43,   43,   43,   43,   35,   19,   19,   19,
        19,   19,   19,   35,   52,   52,   35,   44,   44,   44,
-       44,   53,   53,  190,   45,   87,   87,   87,   87,   87,
-      110,  110,  189,   52,  119,  119,  119,  119,  154,  154,
-       53,  142,  142,  142,  142,  142,  160,  160,  160,  160,
-      177,  177,  177,  177,  184,  184,  188,  185,  182,  181,
-      179,  178,  175,  174,  173,  169,  167,  166,  163,  159,
-
-      158,  157,  156,  184,  155,  153,  152,  184,  198,  151,
-      198,  198,  198,  199,  199,  200,  200,  202,  202,  202,
-      150,  147,  146,  145,  144,  143,  141,  140,  139,  137,
-      136,  135,  132,  131,  130,  129,  128,  127,  125,  124,
-      123,  121,  120,  118,  117,  116,  115,  114,  112,  111,
-      109,  108,  106,  104,  102,  101,  100,   98,   97,   96,
-       94,   93,   92,   86,   85,   84,   83,   82,   81,   80,
-       79,   78,   77,   75,   74,   73,   72,   71,   70,   69,
-       68,   67,   66,   65,   64,   63,   62,   61,   60,   59,
-       58,   57,   49,   47,   46,   41,   40,   39,   38,   37,
-
-       34,   31,   28,   26,   17,   16,    7,    6,    5,    3,
-      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
-      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
-      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
-      197,  197,  197,  197,  197,  197,  197,  197,  197,  197,
-      197,  197,  197,  197,  197
+       44,   53,   53,  198,   45,   87,   87,   87,   87,   87,
+      111,  111,  196,   52,  120,  120,  120,  120,  157,  157,
+       53,  144,  144,  144,  144,  144,  163,  163,  163,  163,
+      181,  181,  181,  181,  188,  188,  194,  193,  192,  189,
+      186,  185,  183,  182,  179,  178,  177,  173,  171,  170,
+
+      166,  162,  161,  188,  160,  159,  158,  188,  202,  156,
+      202,  202,  202,  203,  203,  204,  204,  206,  206,  206,
+      155,  154,  153,  152,  149,  148,  147,  146,  145,  143,
+      142,  141,  139,  138,  137,  134,  133,  132,  131,  130,
+      129,  128,  126,  125,  124,  122,  121,  119,  118,  117,
+      116,  115,  113,  112,  110,  109,  107,  105,  103,  102,
+      101,  100,   98,   97,   96,   94,   93,   92,   86,   85,
+       84,   83,   82,   81,   80,   79,   78,   77,   75,   74,
+       73,   72,   71,   70,   68,   67,   66,   65,   64,   63,
+       62,   61,   60,   59,   58,   57,   49,   47,   46,   41,
+
+       40,   39,   38,   37,   34,   31,   28,   26,   17,   16,
+        7,    6,    5,    3,  201,  201,  201,  201,  201,  201,
+      201,  201,  201,  201,  201,  201,  201,  201,  201,  201,
+      201,  201,  201,  201,  201,  201,  201,  201,  201,  201,
+      201,  201,  201,  201,  201,  201,  201,  201,  201,  201,
+      201,  201,  201,  201,  201,  201,  201,  201,  201
     } ;
 
 /* Table of booleans, true if rule could match eol. */
-static const flex_int32_t yy_rule_can_match_eol[52] =
+static const flex_int32_t yy_rule_can_match_eol[53] =
     {   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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-    0, 0, 0, 0, 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 const flex_int16_t yy_rule_linenum[51] =
+static const flex_int16_t yy_rule_linenum[52] =
     {   0,
       102,  107,  113,  123,  129,  147,  154,  168,  169,  170,
       171,  172,  173,  174,  175,  176,  177,  178,  179,  180,
       181,  182,  183,  184,  185,  186,  187,  188,  189,  190,
       191,  192,  193,  194,  195,  196,  197,  198,  199,  200,
-      201,  202,  203,  204,  205,  206,  207,  208,  209,  210
+      201,  202,  203,  204,  205,  206,  207,  208,  209,  210,
+      211
     } ;
 
 static yy_state_type *yy_state_buf=0, *yy_state_ptr=0;
@@ -1038,7 +1041,7 @@ namespace {
 
 /* To avoid the call to exit... oops! */
 #define YY_FATAL_ERROR(msg) isc::eval::EvalContext::fatal(msg)
-#line 1041 "lexer.cc"
+#line 1044 "lexer.cc"
 /* noyywrap disables automatic rewinding for the next file to parse. Since we
    always parse only a single string, there's no need to do any wraps. And
    using yywrap requires linking with -lfl, which provides the default yywrap
@@ -1063,8 +1066,8 @@ namespace {
    by moving it ahead by yyleng bytes. yyleng specifies the length of the
    currently matched token. */
 #define YY_USER_ACTION  loc.columns(evalleng);
-#line 1066 "lexer.cc"
-#line 1067 "lexer.cc"
+#line 1069 "lexer.cc"
+#line 1070 "lexer.cc"
 
 #define INITIAL 0
 
@@ -1369,7 +1372,7 @@ YY_DECL
 
 
 
-#line 1372 "lexer.cc"
+#line 1375 "lexer.cc"
 
 	while ( /*CONSTCOND*/1 )		/* loops until end-of-file is reached */
 		{
@@ -1397,14 +1400,14 @@ yy_match:
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
-				if ( yy_current_state >= 198 )
+				if ( yy_current_state >= 202 )
 					yy_c = yy_meta[yy_c];
 				}
 			yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
 			*(yy_state_ptr)++ = yy_current_state;
 			++yy_cp;
 			}
-		while ( yy_current_state != 197 );
+		while ( yy_current_state != 201 );
 
 yy_find_action:
 /* %% [10.0] code to find the action number goes here */
@@ -1467,13 +1470,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 < 51 )
+			else if ( yy_act < 52 )
 				fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
 				         (long)yy_rule_linenum[yy_act], yytext );
-			else if ( yy_act == 51 )
+			else if ( yy_act == 52 )
 				fprintf( stderr, "--accepting default rule (\"%s\")\n",
 				         yytext );
-			else if ( yy_act == 52 )
+			else if ( yy_act == 53 )
 				fprintf( stderr, "--(end of buffer or a NUL)\n" );
 			else
 				fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
@@ -1734,68 +1737,73 @@ return isc::eval::EvalParser::make_CONCAT(loc);
 case 40:
 YY_RULE_SETUP
 #line 200 "lexer.ll"
-return isc::eval::EvalParser::make_NOT(loc);
+return isc::eval::EvalParser::make_IFELSE(loc);
 	YY_BREAK
 case 41:
 YY_RULE_SETUP
 #line 201 "lexer.ll"
-return isc::eval::EvalParser::make_AND(loc);
+return isc::eval::EvalParser::make_NOT(loc);
 	YY_BREAK
 case 42:
 YY_RULE_SETUP
 #line 202 "lexer.ll"
-return isc::eval::EvalParser::make_OR(loc);
+return isc::eval::EvalParser::make_AND(loc);
 	YY_BREAK
 case 43:
 YY_RULE_SETUP
 #line 203 "lexer.ll"
-return isc::eval::EvalParser::make_DOT(loc);
+return isc::eval::EvalParser::make_OR(loc);
 	YY_BREAK
 case 44:
 YY_RULE_SETUP
 #line 204 "lexer.ll"
-return isc::eval::EvalParser::make_LPAREN(loc);
+return isc::eval::EvalParser::make_DOT(loc);
 	YY_BREAK
 case 45:
 YY_RULE_SETUP
 #line 205 "lexer.ll"
-return isc::eval::EvalParser::make_RPAREN(loc);
+return isc::eval::EvalParser::make_LPAREN(loc);
 	YY_BREAK
 case 46:
 YY_RULE_SETUP
 #line 206 "lexer.ll"
-return isc::eval::EvalParser::make_LBRACKET(loc);
+return isc::eval::EvalParser::make_RPAREN(loc);
 	YY_BREAK
 case 47:
 YY_RULE_SETUP
 #line 207 "lexer.ll"
-return isc::eval::EvalParser::make_RBRACKET(loc);
+return isc::eval::EvalParser::make_LBRACKET(loc);
 	YY_BREAK
 case 48:
 YY_RULE_SETUP
 #line 208 "lexer.ll"
-return isc::eval::EvalParser::make_COMA(loc);
+return isc::eval::EvalParser::make_RBRACKET(loc);
 	YY_BREAK
 case 49:
 YY_RULE_SETUP
 #line 209 "lexer.ll"
-return isc::eval::EvalParser::make_ANY(loc);
+return isc::eval::EvalParser::make_COMA(loc);
 	YY_BREAK
 case 50:
 YY_RULE_SETUP
 #line 210 "lexer.ll"
+return isc::eval::EvalParser::make_ANY(loc);
+	YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 211 "lexer.ll"
 driver.error (loc, "Invalid character: " + std::string(evaltext));
 	YY_BREAK
 case YY_STATE_EOF(INITIAL):
-#line 211 "lexer.ll"
+#line 212 "lexer.ll"
 return isc::eval::EvalParser::make_END(loc);
 	YY_BREAK
-case 51:
+case 52:
 YY_RULE_SETUP
-#line 212 "lexer.ll"
+#line 213 "lexer.ll"
 ECHO;
 	YY_BREAK
-#line 1798 "lexer.cc"
+#line 1806 "lexer.cc"
 
 	case YY_END_OF_BUFFER:
 		{
@@ -2081,7 +2089,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 >= 198 )
+			if ( yy_current_state >= 202 )
 				yy_c = yy_meta[yy_c];
 			}
 		yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
@@ -2109,11 +2117,11 @@ static int yy_get_next_buffer (void)
 	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 		{
 		yy_current_state = (int) yy_def[yy_current_state];
-		if ( yy_current_state >= 198 )
+		if ( yy_current_state >= 202 )
 			yy_c = yy_meta[yy_c];
 		}
 	yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
-	yy_is_jam = (yy_current_state == 197);
+	yy_is_jam = (yy_current_state == 201);
 	if ( ! yy_is_jam )
 		*(yy_state_ptr)++ = yy_current_state;
 
@@ -2879,7 +2887,7 @@ void yyfree (void * ptr )
 
 /* %ok-for-header */
 
-#line 212 "lexer.ll"
+#line 213 "lexer.ll"
 
 
 using namespace isc::eval;

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

@@ -196,6 +196,7 @@ addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]*
 "substring"    return isc::eval::EvalParser::make_SUBSTRING(loc);
 "all"          return isc::eval::EvalParser::make_ALL(loc);
 "concat"       return isc::eval::EvalParser::make_CONCAT(loc);
+"ifelse"       return isc::eval::EvalParser::make_IFELSE(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);

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

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

File diff suppressed because it is too large
+ 325 - 315
src/lib/eval/parser.cc


+ 90 - 79
src/lib/eval/parser.h

@@ -218,7 +218,7 @@ namespace isc { namespace eval {
     /// Both variants must be built beforehand, because swapping the actual
     /// data requires reading it (with as()), and this is not possible on
     /// unconstructed variants: it would require some dynamic testing, which
-    /// should not be the variant's responsibility.
+    /// should not be the variant's responsability.
     /// Swapping between built and (possibly) non-built is done with
     /// variant::move ().
     template <typename T>
@@ -391,21 +391,22 @@ namespace isc { namespace eval {
         TOKEN_ALL = 289,
         TOKEN_COMA = 290,
         TOKEN_CONCAT = 291,
-        TOKEN_PKT6 = 292,
-        TOKEN_MSGTYPE = 293,
-        TOKEN_TRANSID = 294,
-        TOKEN_VENDOR_CLASS = 295,
-        TOKEN_VENDOR = 296,
-        TOKEN_ANY = 297,
-        TOKEN_DATA = 298,
-        TOKEN_ENTERPRISE = 299,
-        TOKEN_TOPLEVEL_BOOL = 300,
-        TOKEN_TOPLEVEL_STRING = 301,
-        TOKEN_STRING = 302,
-        TOKEN_INTEGER = 303,
-        TOKEN_HEXSTRING = 304,
-        TOKEN_OPTION_NAME = 305,
-        TOKEN_IP_ADDRESS = 306
+        TOKEN_IFELSE = 292,
+        TOKEN_PKT6 = 293,
+        TOKEN_MSGTYPE = 294,
+        TOKEN_TRANSID = 295,
+        TOKEN_VENDOR_CLASS = 296,
+        TOKEN_VENDOR = 297,
+        TOKEN_ANY = 298,
+        TOKEN_DATA = 299,
+        TOKEN_ENTERPRISE = 300,
+        TOKEN_TOPLEVEL_BOOL = 301,
+        TOKEN_TOPLEVEL_STRING = 302,
+        TOKEN_STRING = 303,
+        TOKEN_INTEGER = 304,
+        TOKEN_HEXSTRING = 305,
+        TOKEN_OPTION_NAME = 306,
+        TOKEN_IP_ADDRESS = 307
       };
     };
 
@@ -670,6 +671,10 @@ namespace isc { namespace eval {
 
     static inline
     symbol_type
+    make_IFELSE (const location_type& l);
+
+    static inline
+    symbol_type
     make_PKT6 (const location_type& l);
 
     static inline
@@ -933,12 +938,12 @@ namespace isc { namespace eval {
     enum
     {
       yyeof_ = 0,
-      yylast_ = 190,     ///< Last index in yytable_.
+      yylast_ = 188,     ///< Last index in yytable_.
       yynnts_ = 16,  ///< Number of nonterminal symbols.
-      yyfinal_ = 30, ///< Termination state number.
+      yyfinal_ = 31, ///< Termination state number.
       yyterror_ = 1,
       yyerrcode_ = 256,
-      yyntokens_ = 52  ///< Number of tokens.
+      yyntokens_ = 53  ///< Number of tokens.
     };
 
 
@@ -985,9 +990,9 @@ namespace isc { namespace eval {
       15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
       25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
       35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
-      45,    46,    47,    48,    49,    50,    51
+      45,    46,    47,    48,    49,    50,    51,    52
     };
-    const unsigned int user_token_number_max_ = 306;
+    const unsigned int user_token_number_max_ = 307;
     const token_number_type undef_token_ = 2;
 
     if (static_cast<int>(t) <= yyeof_)
@@ -1020,44 +1025,44 @@ namespace isc { namespace eval {
   {
       switch (other.type_get ())
     {
-      case 59: // option_repr_type
+      case 60: // option_repr_type
         value.copy< TokenOption::RepresentationType > (other.value);
         break;
 
-      case 63: // pkt4_field
+      case 64: // pkt4_field
         value.copy< TokenPkt4::FieldType > (other.value);
         break;
 
-      case 64: // pkt6_field
+      case 65: // pkt6_field
         value.copy< TokenPkt6::FieldType > (other.value);
         break;
 
-      case 61: // pkt_metadata
+      case 62: // pkt_metadata
         value.copy< TokenPkt::MetadataType > (other.value);
         break;
 
-      case 65: // relay6_field
+      case 66: // relay6_field
         value.copy< TokenRelay6Field::FieldType > (other.value);
         break;
 
-      case 60: // nest_level
+      case 61: // nest_level
         value.copy< int8_t > (other.value);
         break;
 
-      case 47: // "constant string"
-      case 48: // "integer"
-      case 49: // "constant hexstring"
-      case 50: // "option name"
-      case 51: // "ip address"
+      case 48: // "constant string"
+      case 49: // "integer"
+      case 50: // "constant hexstring"
+      case 51: // "option name"
+      case 52: // "ip address"
         value.copy< std::string > (other.value);
         break;
 
-      case 58: // option_code
+      case 59: // option_code
         value.copy< uint16_t > (other.value);
         break;
 
-      case 57: // integer_expr
-      case 62: // enterprise_id
+      case 58: // integer_expr
+      case 63: // enterprise_id
         value.copy< uint32_t > (other.value);
         break;
 
@@ -1078,44 +1083,44 @@ namespace isc { namespace eval {
     (void) v;
       switch (this->type_get ())
     {
-      case 59: // option_repr_type
+      case 60: // option_repr_type
         value.copy< TokenOption::RepresentationType > (v);
         break;
 
-      case 63: // pkt4_field
+      case 64: // pkt4_field
         value.copy< TokenPkt4::FieldType > (v);
         break;
 
-      case 64: // pkt6_field
+      case 65: // pkt6_field
         value.copy< TokenPkt6::FieldType > (v);
         break;
 
-      case 61: // pkt_metadata
+      case 62: // pkt_metadata
         value.copy< TokenPkt::MetadataType > (v);
         break;
 
-      case 65: // relay6_field
+      case 66: // relay6_field
         value.copy< TokenRelay6Field::FieldType > (v);
         break;
 
-      case 60: // nest_level
+      case 61: // nest_level
         value.copy< int8_t > (v);
         break;
 
-      case 47: // "constant string"
-      case 48: // "integer"
-      case 49: // "constant hexstring"
-      case 50: // "option name"
-      case 51: // "ip address"
+      case 48: // "constant string"
+      case 49: // "integer"
+      case 50: // "constant hexstring"
+      case 51: // "option name"
+      case 52: // "ip address"
         value.copy< std::string > (v);
         break;
 
-      case 58: // option_code
+      case 59: // option_code
         value.copy< uint16_t > (v);
         break;
 
-      case 57: // integer_expr
-      case 62: // enterprise_id
+      case 58: // integer_expr
+      case 63: // enterprise_id
         value.copy< uint32_t > (v);
         break;
 
@@ -1223,44 +1228,44 @@ namespace isc { namespace eval {
     // Type destructor.
     switch (yytype)
     {
-      case 59: // option_repr_type
+      case 60: // option_repr_type
         value.template destroy< TokenOption::RepresentationType > ();
         break;
 
-      case 63: // pkt4_field
+      case 64: // pkt4_field
         value.template destroy< TokenPkt4::FieldType > ();
         break;
 
-      case 64: // pkt6_field
+      case 65: // pkt6_field
         value.template destroy< TokenPkt6::FieldType > ();
         break;
 
-      case 61: // pkt_metadata
+      case 62: // pkt_metadata
         value.template destroy< TokenPkt::MetadataType > ();
         break;
 
-      case 65: // relay6_field
+      case 66: // relay6_field
         value.template destroy< TokenRelay6Field::FieldType > ();
         break;
 
-      case 60: // nest_level
+      case 61: // nest_level
         value.template destroy< int8_t > ();
         break;
 
-      case 47: // "constant string"
-      case 48: // "integer"
-      case 49: // "constant hexstring"
-      case 50: // "option name"
-      case 51: // "ip address"
+      case 48: // "constant string"
+      case 49: // "integer"
+      case 50: // "constant hexstring"
+      case 51: // "option name"
+      case 52: // "ip address"
         value.template destroy< std::string > ();
         break;
 
-      case 58: // option_code
+      case 59: // option_code
         value.template destroy< uint16_t > ();
         break;
 
-      case 57: // integer_expr
-      case 62: // enterprise_id
+      case 58: // integer_expr
+      case 63: // enterprise_id
         value.template destroy< uint32_t > ();
         break;
 
@@ -1287,44 +1292,44 @@ namespace isc { namespace eval {
     super_type::move(s);
       switch (this->type_get ())
     {
-      case 59: // option_repr_type
+      case 60: // option_repr_type
         value.move< TokenOption::RepresentationType > (s.value);
         break;
 
-      case 63: // pkt4_field
+      case 64: // pkt4_field
         value.move< TokenPkt4::FieldType > (s.value);
         break;
 
-      case 64: // pkt6_field
+      case 65: // pkt6_field
         value.move< TokenPkt6::FieldType > (s.value);
         break;
 
-      case 61: // pkt_metadata
+      case 62: // pkt_metadata
         value.move< TokenPkt::MetadataType > (s.value);
         break;
 
-      case 65: // relay6_field
+      case 66: // relay6_field
         value.move< TokenRelay6Field::FieldType > (s.value);
         break;
 
-      case 60: // nest_level
+      case 61: // nest_level
         value.move< int8_t > (s.value);
         break;
 
-      case 47: // "constant string"
-      case 48: // "integer"
-      case 49: // "constant hexstring"
-      case 50: // "option name"
-      case 51: // "ip address"
+      case 48: // "constant string"
+      case 49: // "integer"
+      case 50: // "constant hexstring"
+      case 51: // "option name"
+      case 52: // "ip address"
         value.move< std::string > (s.value);
         break;
 
-      case 58: // option_code
+      case 59: // option_code
         value.move< uint16_t > (s.value);
         break;
 
-      case 57: // integer_expr
-      case 62: // enterprise_id
+      case 58: // integer_expr
+      case 63: // enterprise_id
         value.move< uint32_t > (s.value);
         break;
 
@@ -1388,7 +1393,7 @@ namespace isc { namespace eval {
      275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
      285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
      295,   296,   297,   298,   299,   300,   301,   302,   303,   304,
-     305,   306
+     305,   306,   307
     };
     return static_cast<token_type> (yytoken_number_[type]);
   }
@@ -1604,6 +1609,12 @@ namespace isc { namespace eval {
   }
 
   EvalParser::symbol_type
+  EvalParser::make_IFELSE (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_IFELSE, l);
+  }
+
+  EvalParser::symbol_type
   EvalParser::make_PKT6 (const location_type& l)
   {
     return symbol_type (token::TOKEN_PKT6, l);
@@ -1696,7 +1707,7 @@ namespace isc { namespace eval {
 
 #line 14 "parser.yy" // lalr1.cc:377
 } } // isc::eval
-#line 1700 "parser.h" // lalr1.cc:377
+#line 1711 "parser.h" // lalr1.cc:377
 
 
 

+ 6 - 0
src/lib/eval/parser.yy

@@ -72,6 +72,7 @@ using namespace isc::eval;
   ALL "all"
   COMA ","
   CONCAT "concat"
+  IFELSE "ifelse"
   PKT6 "pkt6"
   MSGTYPE "msgtype"
   TRANSID "transid"
@@ -327,6 +328,11 @@ string_expr : STRING
                       TokenPtr conc(new TokenConcat());
                       ctx.expression.push_back(conc);
                   }
+            | IFELSE "(" bool_expr "," string_expr "," string_expr ")"
+                  {
+                      TokenPtr cond(new TokenIfElse());
+                      ctx.expression.push_back(cond);
+                  }
             | VENDOR "." ENTERPRISE
                 {
                     // expression: vendor.enterprise

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

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

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

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

+ 40 - 0
src/lib/eval/tests/context_unittest.cc

@@ -406,6 +406,14 @@ public:
         EXPECT_TRUE(conc);
     }
 
+    /// @brief checks if the given token is an ifelse operator
+    void checkTokenIfElse(const TokenPtr& token) {
+        ASSERT_TRUE(token);
+        boost::shared_ptr<TokenIfElse> alt =
+            boost::dynamic_pointer_cast<TokenIfElse>(token);
+        EXPECT_TRUE(alt);
+    }
+
     /// @brief checks if the given expression raises the expected message
     /// when it is parsed.
     void checkError(const string& expr, const string& msg) {
@@ -1209,6 +1217,26 @@ TEST_F(EvalContextTest, concat) {
     checkTokenConcat(tmp3);
 }
 
+// Test the parsing of an ifelse expression
+TEST_F(EvalContextTest, ifElse) {
+    EvalContext eval(Option::V4);
+
+    EXPECT_NO_THROW(parsed_ =
+        eval.parseString("ifelse('foo' == 'bar', 'us', 'them') == 'you'"));
+
+    ASSERT_EQ(8, eval.expression.size());
+
+    TokenPtr tmp1 = eval.expression.at(2);
+    TokenPtr tmp2 = eval.expression.at(3);
+    TokenPtr tmp3 = eval.expression.at(4);
+    TokenPtr tmp4 = eval.expression.at(5);
+
+    checkTokenEq(tmp1);
+    checkTokenString(tmp2, "us");
+    checkTokenString(tmp3, "them");
+    checkTokenIfElse(tmp4);
+}
+
 //
 // Test some scanner error cases
 TEST_F(EvalContextTest, scanErrors) {
@@ -1358,6 +1386,10 @@ TEST_F(EvalContextTest, parseErrors) {
                "<string>:1.16: syntax error, unexpected ), expecting \",\"");
     checkError("concat('foo','bar','') == 'foobar'",
                "<string>:1.19: syntax error, unexpected \",\", expecting )");
+    checkError("ifelse('foo'=='bar','foo')",
+               "<string>:1.26: syntax error, unexpected ), expecting \",\"");
+    checkError("ifelse('foo'=='bar','foo','bar','')",
+               "<string>:1.32: syntax error, unexpected \",\", expecting )");
 }
 
 // Tests some type error cases
@@ -1388,6 +1420,14 @@ TEST_F(EvalContextTest, typeErrors) {
                "<string>:1.8-10: syntax error, unexpected and, expecting ==");
     checkError("'true' or 'false'",
                "<string>:1.8-9: syntax error, unexpected or, expecting ==");
+    // Ifelse requires a boolean condition and string branches.
+    checkError("ifelse('foobar','foo','bar')",
+               "<string>:1.16: syntax error, unexpected \",\", expecting ==");
+    checkError("ifelse('foo'=='bar','foo'=='foo','bar')",
+               "<string>:1.26-27: syntax error, unexpected ==, "
+               "expecting \",\"");
+    checkError("ifelse('foo'=='bar','foo','bar'=='bar')",
+               "<string>:1.32-33: syntax error, unexpected ==, expecting )");
 }
 
 

+ 14 - 0
src/lib/eval/tests/evaluate_unittest.cc

@@ -490,6 +490,20 @@ TEST_F(ExpressionsTest, evaluateString) {
                                            EvalContext::PARSER_STRING);
     testExpressionNegative<EvalParseError>("pkt6.msgtype == 1", Option::V6,
                                            EvalContext::PARSER_STRING);
+
+    // Check that ifelse works as expecting (it was added explicitely for
+    // the string evaluation).
+    testExpressionString(Option::V4,
+                         "ifelse(option[100].exists,'foo','bar')", "foo");
+    testExpressionString(Option::V4,
+                         "ifelse(option[200].exists,'foo','bar')", "bar");
+
+    // Check that ifelse can be chained.
+    testExpressionString(Option::V4,
+                         "ifelse(option[200].exists,option[200].hex,"
+                                 "ifelse(option[100].exists,"
+                                         "option[100].hex,'none???'))",
+                         "hundred4");
 }
 
 };

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

@@ -1954,6 +1954,45 @@ TEST_F(TokenTest, concat) {
     EXPECT_TRUE(checkFile());
 }
 
+// This test checks if a token representing an ifelse is able
+// to select the branch following the condition.
+TEST_F(TokenTest, ifElse) {
+    ASSERT_NO_THROW(t_.reset(new TokenIfElse()));
+
+    // Ifelse requires three values on the stack, try
+    // with 0, 1 and 2 all should throw an exception
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+    values_.push("bar");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+    values_.push("foo");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+    // The condition must be a boolean
+    values_.push("bar");
+    EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
+
+    // Check if what it returns
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenIfElse()));
+    values_.push("true");
+    values_.push("foo");
+    values_.push("bar");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("foo", values_.top());
+
+    clearStack();
+    ASSERT_NO_THROW(t_.reset(new TokenIfElse()));
+    values_.push("false");
+    values_.push("foo");
+    values_.push("bar");
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(1, values_.size());
+    EXPECT_EQ("bar", 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, operatorNotInvalid) {

+ 36 - 0
src/lib/eval/token.cc

@@ -592,6 +592,42 @@ TokenConcat::evaluate(Pkt& /*pkt*/, ValueStack& values) {
 }
 
 void
+TokenIfElse::evaluate(Pkt& /*pkt*/, ValueStack& values) {
+
+    if (values.size() < 3) {
+        isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
+                  "3 values for ifelse, got " << values.size());
+    }
+
+    string iffalse = values.top();
+    values.pop();
+    string iftrue = values.top();
+    values.pop();
+    string cond = values.top();
+    values.pop();
+    bool val = toBool(cond);
+
+    if (val) {
+        values.push(iftrue);
+    } else {
+        values.push(iffalse);
+    }
+
+    // Log what we popped and pushed
+    if (val) {
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IFELSE_TRUE)
+            .arg('\'' + cond + '\'')
+            .arg(toHex(iffalse))
+            .arg(toHex(iftrue));
+    } else {
+        LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IFELSE_FALSE)
+            .arg('\'' +cond + '\'')
+            .arg(toHex(iftrue))
+            .arg(toHex(iffalse));
+    }
+}
+
+void
 TokenNot::evaluate(Pkt& /*pkt*/, ValueStack& values) {
 
     if (values.size() == 0) {

+ 31 - 0
src/lib/eval/token.h

@@ -694,6 +694,37 @@ public:
     void evaluate(Pkt& pkt, ValueStack& values);
 };
 
+/// @brief Token that represents an alternative
+///
+/// For example in the sub-expression "ifelse(cond, iftrue, iffalse)"
+/// the boolean "cond" expression is evaluated, if it is true then
+/// the "iftrue" value is returned else the "iffalse" value is returned.
+/// Please note that "iftrue" and "iffalse" must be plain string (vs. boolean)
+/// expressions and they are always evaluated. If you want a similar
+/// operator on boolean expressions it can be built from "and", "or" and
+/// "not" boolean operators.
+class TokenIfElse : public Token {
+public:
+    /// @brief Constructor (does nothing)
+    TokenIfElse() { }
+
+    /// @brief Alternative.
+    ///
+    /// Evaluation does not use packet information, but rather consumes the
+    /// last three results. It does a simple string comparison on the
+    /// condition (third value on the stack) which is required to be
+    /// either "true" or "false", and leaves the second and first
+    /// value if the condition is "true" or "false".
+    ///
+    /// @throw EvalBadStack if there are less than 3 values on stack
+    /// @throw EvalTypeError if the third value (the condition) is not
+    ///        either "true" or "false"
+    ///
+    /// @param pkt (unused)
+    /// @param values - stack of values (two items are removed)
+    void evaluate(Pkt& pkt, ValueStack& values);
+};
+
 /// @brief Token that represents logical negation operator
 ///
 /// For example in the expression "not(option[vendor-class].text == 'MSF')"