Added support for CASE expressions - patches from Dan Kennedy. (CVS 437)

FossilOrigin-Name: 836b59d057c3fb4087b138c9bfbc03392ddfb89d
diff --git a/src/expr.c b/src/expr.c
index 793a44e..5b0e732 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -12,7 +12,7 @@
 ** This file contains routines used for analyzing expressions and
 ** for generating VDBE code that evaluates expressions in SQLite.
 **
-** $Id: expr.c,v 1.56 2002/03/13 18:54:07 drh Exp $
+** $Id: expr.c,v 1.57 2002/03/24 13:13:29 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -877,8 +877,49 @@
       sqliteExprCode(pParse, pExpr->pLeft);
       break;
     }
+    case TK_CASE: {
+      int expr_end_label;
+      int next_when_label;
+      int i;
+
+      assert(pExpr->pList);
+      assert((pExpr->pList->nExpr % 2) == 0);
+      assert(pExpr->pList->nExpr > 0);
+      expr_end_label = sqliteVdbeMakeLabel(pParse->pVdbe);
+      if( pExpr->pLeft ){
+        sqliteExprCode(pParse, pExpr->pLeft);
+      }
+      for(i=0; i<pExpr->pList->nExpr; i=i+2){
+        if( i!=0 ){
+          sqliteVdbeResolveLabel(pParse->pVdbe, next_when_label);
+        }
+        next_when_label = sqliteVdbeMakeLabel(pParse->pVdbe);
+        if( pExpr->pLeft ){
+          sqliteVdbeAddOp(pParse->pVdbe, OP_Dup, 0, 1);
+          sqliteExprCode(pParse, pExpr->pList->a[i].pExpr);
+          sqliteVdbeAddOp(pParse->pVdbe, OP_Ne, 0, next_when_label);
+        }else{
+          sqliteExprIfFalse(pParse, pExpr->pList->a[i].pExpr, next_when_label);
+        }
+        if( pExpr->pLeft ){
+          sqliteVdbeAddOp(pParse->pVdbe, OP_Pop, 1, 0);
+        }
+        sqliteExprCode(pParse, pExpr->pList->a[i+1].pExpr);
+        sqliteVdbeAddOp(pParse->pVdbe, OP_Goto, 0, expr_end_label);
+      }
+      sqliteVdbeResolveLabel(pParse->pVdbe, next_when_label);
+      if( pExpr->pLeft ){
+        sqliteVdbeAddOp(pParse->pVdbe, OP_Pop, 1, 0);
+      }
+      if( pExpr->pRight ){
+        sqliteExprCode(pParse, pExpr->pRight);
+      }else{
+        sqliteVdbeAddOp(pParse->pVdbe, OP_String, 0, 0);
+      }
+      sqliteVdbeResolveLabel(pParse->pVdbe, expr_end_label);
+    }
+    break;
   }
-  return;
 }
 
 /*
diff --git a/src/parse.y b/src/parse.y
index ff24a58..2343d04 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -14,7 +14,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.57 2002/03/13 18:54:08 drh Exp $
+** @(#) $Id: parse.y,v 1.58 2002/03/24 13:13:29 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -520,7 +520,28 @@
   sqliteExprSpan(A,&X->span,&E);
 }
 
-
+/* CASE expressions */
+expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
+  A = sqliteExpr(TK_CASE, X, Z, 0);
+  if( A ) A->pList = Y;
+  sqliteExprSpan(A, &C, &E);
+}
+%type case_exprlist {ExprList*}
+%destructor case_exprlist {sqliteExprListDelete($$);}
+case_exprlist(A) ::= case_exprlist(X) WHEN expr(Y) THEN expr(Z). {
+  A = sqliteExprListAppend(X, Y, 0);
+  A = sqliteExprListAppend(A, Z, 0);
+}
+case_exprlist(A) ::= WHEN expr(Y) THEN expr(Z). {
+  A = sqliteExprListAppend(0, Y, 0);
+  A = sqliteExprListAppend(A, Z, 0);
+}
+%type case_else {Expr*}
+case_else(A) ::=  ELSE expr(X).         {A = X;}
+case_else(A) ::=  .                     {A = 0;} 
+%type case_operand {Expr*}
+case_operand(A) ::= expr(X).            {A = X;} 
+case_operand(A) ::= .                   {A = 0;} 
 
 %type exprlist {ExprList*}
 %destructor exprlist {sqliteExprListDelete($$);}
diff --git a/src/tokenize.c b/src/tokenize.c
index 5a541cf..84eaab7 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -15,7 +15,7 @@
 ** individual tokens and sends those tokens one-by-one over to the
 ** parser for analysis.
 **
-** $Id: tokenize.c,v 1.39 2002/03/13 18:54:08 drh Exp $
+** $Id: tokenize.c,v 1.40 2002/03/24 13:13:29 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -46,6 +46,7 @@
   { "BEGIN",             0, TK_BEGIN,            0 },
   { "BETWEEN",           0, TK_BETWEEN,          0 },
   { "BY",                0, TK_BY,               0 },
+  { "CASE",              0, TK_CASE,             0 },
   { "CHECK",             0, TK_CHECK,            0 },
   { "CLUSTER",           0, TK_CLUSTER,          0 },
   { "COMMIT",            0, TK_COMMIT,           0 },
@@ -60,6 +61,7 @@
   { "DISTINCT",          0, TK_DISTINCT,         0 },
   { "DROP",              0, TK_DROP,             0 },
   { "END",               0, TK_END,              0 },
+  { "ELSE",              0, TK_ELSE,             0 },
   { "EXCEPT",            0, TK_EXCEPT,           0 },
   { "EXPLAIN",           0, TK_EXPLAIN,          0 },
   { "FAIL",              0, TK_FAIL,             0 },
@@ -94,6 +96,7 @@
   { "TABLE",             0, TK_TABLE,            0 },
   { "TEMP",              0, TK_TEMP,             0 },
   { "TEMPORARY",         0, TK_TEMP,             0 },
+  { "THEN",              0, TK_THEN,             0 },
   { "TRANSACTION",       0, TK_TRANSACTION,      0 },
   { "UNION",             0, TK_UNION,            0 },
   { "UNIQUE",            0, TK_UNIQUE,           0 },
@@ -102,6 +105,7 @@
   { "VACUUM",            0, TK_VACUUM,           0 },
   { "VALUES",            0, TK_VALUES,           0 },
   { "VIEW",              0, TK_VIEW,             0 },
+  { "WHEN",              0, TK_WHEN,             0 },
   { "WHERE",             0, TK_WHERE,            0 },
 };