/*
_____       _    _    Corso   Italia,  178
(_|__   .  (_   |_|_  56125           Pisa
(_|_) |)|(()_)()| |   tel.  +39  050 46380
  |   |               picosoft@picosoft.it

 Copyright (C) Picosoft s.r.l. 1995-2002

 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
 the Free Software Foundation; either version 2, or (at your option)
 any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
# include "express.h"
static char rcsid[] = "$Id: express.cpp 5.2 2001/04/18 15:28:16 picoSoft Exp Marco $";
static char rcsidh[] = express_h;
# include "piteratr.h"
# include "token.h"
# include "sqlpars.h"
# include "perror.h"
# include <stdlib.h>
# include <math.h>
extern "C" {
# include "picoend.h"
}

PCLASSID(Expression)

Expression::Expression (SqlParser &s, Lex &l, Method m, PBool aggr) :
parent (&s),
parseAggregate(aggr)
{
   int numeric;
   StackItem *si;
   Token tk;
   aggregate = NULL_;
   value = 0;
   aggrValue = 0;
   findAttr = m;

   if (Start(l) == SQL_SUCCESS) {
      parsing = PTrue;
      for (si = stack.GetFirst(); si; si = stack.GetNext())
         if (si->sa != 0) {
            if (si->sa->GetStmtParent() != 0)
               DbField.AddItem (si->sa);
         }
      if (stack.GetItemNum() > 1) {
         if (verify (numeric, PTrue) != SQL_ERROR) {
            if (numeric) { 
               tk.word = "0";
               tk.token = NUMBER;
            } else {
               PString dummy (257, ' ');
               tk.word = dummy;
               tk.token = STR_LITERAL;
            }
            value = new StmtAttr (*(parent->GetDB()), tk); 
         } else {
            parsing = PFalse;
         }
      } else {
         value = stack.GetFirst()->sa;
      }
      if (parsing) {
         switch (aggregate) {
         case COUNT:
            tk.word = "0";
            tk.token = NUMBER;
            aggrValue = new StmtAttr (*(parent->GetDB()), tk);
            if (name.size() == 0)
               name = "count(" + value->getName() + ")";
            aggrValue->setAggregate (aggregate);
            break;
         case MAX:
            aggrValue = new StmtAttr (*value);
            if (name.size() == 0)
               name = "max(" + value->getName() + ")";
            aggrValue->setAggregate (aggregate);
            break;
         case MIN:
            aggrValue = new StmtAttr (*value);
            if (name.size() == 0)
               name = "min(" + value->getName() + ")";
            aggrValue->setAggregate (aggregate);
            break;
         case SUM:
            if (!value->getAttr()->IsNumeric()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid SUM argument");
               parsing = PFalse;
            } else {
               tk.word = "0";
               tk.token = NUMBER;
               aggrValue = new StmtAttr (*(parent->GetDB()), tk);
               if (name.size() == 0)
                  name = "sum(" + value->getName() + ")";
               aggrValue->setAggregate (aggregate);
            }
            break;
         case AVG:
            if (!value->getAttr()->IsNumeric()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid AVG argument");
               parsing = PFalse;
            } else {
               tk.word = "0";
               tk.token = NUMBER;
               aggrValue = new StmtAttr (*(parent->GetDB()), tk);
               if (name.size() == 0)
                  name = "avg(" + value->getName() + ")";
               aggrValue->setAggregate (aggregate);
            }
            break;
         default:
            aggrValue = value;
            if (name.size() == 0) {
               if (stack.GetItemNum() > 1) {
                  name = "(expression)";
               } else
                  name = value->getName();
            }
         }
      }
   } else
      parsing = PFalse;
}

Expression::Expression (SqlParser *s, StmtAttr *sa) :
parent (s),
parseAggregate(PFalse)
{
   aggregate = NULL_;
   value = sa;
   aggrValue = value;
   name = value->getName();
   parsing = PTrue;
   stack.AddItem (new StackItem(sa));
   DbField.AddItem (sa);
}

Expression::Expression (const DataBase &d, const Token &t) :
parent (0),
parseAggregate(PFalse)
{
   aggregate = NULL_;
   parsing = PTrue;
   stack.AddItem (new StackItem(d, t));
   value = stack.GetFirst()->sa;
   aggrValue = value;
   name = value->getName();
}

Expression::Expression (Expression &ex) :
parent (ex.parent), 
parseAggregate(ex.parseAggregate)
{
   StackItem *si;
   name = ex.name;
   parsing = ex.parsing;
   aggregate = ex.aggregate;
   findAttr = ex.findAttr;
   value = 0;
   aggrValue = 0;
   for (si = ex.stack.GetFirst(); si; si = ex.stack.GetNext()) {
      stack.AddItem(si = new StackItem (*si));
      if (si->sa != 0)
         if (si->sa->GetStmtParent() != 0)
            DbField.AddItem (si->sa);
   }
   if (stack.GetItemNum() > 1)
      value = new StmtAttr (*ex.value);
   else
      value = stack.GetFirst()->sa;
   if (ex.aggrValue == ex.value)
      aggrValue = value;
   else
      aggrValue = new StmtAttr (*ex.aggrValue);
}

Expression::~Expression ()
{
   StackItem *it;
   StmtAttr *sa;
    
   if (stack.GetItemNum() > 0)
      sa = stack.GetFirst()->sa;
   else
      sa = 0;
   if (aggrValue && aggrValue != value)
      delete aggrValue;
   if (value && value != sa)
      delete value;
   for (it = stack.GetFirst(); it; it = stack.DeleteCurrent())
      delete it;
   for (sa = DistField.GetFirst(); sa; sa = DistField.DeleteCurrent())
      ;
}

Error
Expression::Start (Lex &l)
{
   PStack(StackItem) localStack;
   Error Return = SQL_SUCCESS;

   Token *t = l.GetToken();
   if (t) {
      switch (t->token) {
      case AVG:
      case MIN:
      case MAX:
      case SUM:
         aggregate = t->token;
         if (parseAggregate) {
            t = l.GetToken();
            if (t->token != '(') {
               PrintError (E_SYNTAX_ERROR_000, "expected '(' found: '" + t->word + "'");
               Return = SQL_ERROR;
            } else {
               t = l.GetToken();
               if (t && t->token == DISTINCT) {
                  l.UngetToken();
                  l.UngetToken();
                  StmtAttr *sa = DistinctFunction (l);
                  if (sa) {
                     localStack.Push(new StackItem(sa));
                     DistField.AddItem (sa);
                     reduce (0, localStack);
                     Return = Alias(l);
                  } else {
                     Return = SQL_ERROR;
                  }
               } else {
                  l.UngetToken ();
                  if ((Return = Parenthesis (l)) == SQL_SUCCESS)
                     Return = Alias (l);
               }
            }
         } else {
            PrintError (E_SYNTAX_ERROR_000, "Aggregate function not allowed in that context");
            Return = SQL_ERROR;
         }
         break;
      case COUNT:
         aggregate = t->token;
         if (parseAggregate) {
            t = l.GetToken();
            if (t->token != '(') {
               PrintError (E_SYNTAX_ERROR_000, "expected '(' found: '" + t->word + "'");
               Return = SQL_ERROR;
               break;
            } else {
               t = l.GetToken();
               if (t->token == (toknum) '*') {
                  t = l.GetToken();
                  if (t->token != (toknum) ')') {
                     PrintError (E_SYNTAX_ERROR_000, "expected ')' found: '" + t->word + "'");
                     Return = SQL_ERROR;
                     break;
                  } else { 
                     Token tk;
                     tk.word = "'*'";
                     tk.token = STR_LITERAL;
                     stack.AddItem(new StackItem(*(parent->GetDB()), tk));
                     Return = Alias (l);
                  }
               } else if (t && t->token == DISTINCT) {
                  l.UngetToken();
                  l.UngetToken();
                  StmtAttr *sa = DistinctFunction (l);
                  if (sa) {
                     localStack.Push(new StackItem(sa));
                     DistField.AddItem (sa);
                     reduce (0, localStack);
                     Return = Alias(l);
                  } else {
                     Return = SQL_ERROR;
                  }
               } else {
                  l.UngetToken ();
                  if ((Return = Parenthesis (l)) == SQL_SUCCESS)
                     Return = Alias (l);
               }
            }
         } else {
            PrintError (E_SYNTAX_ERROR_000, "Aggregate function not allowed in that context");
            Return = SQL_ERROR;
         }
         break;
      default:
         l.UngetToken ();
         Return = Item (l, localStack);
         if (Return == SQL_SUCCESS)
            reduce (0, localStack);
         break;
      }
   } else {
      PrintError (E_SYNTAX_ERROR_000, "Unexpected end of statement");
      Return = SQL_ERROR;
   }
   while (localStack.Size() > 0)
      delete localStack.Pop();
   return Return;
}

Error
Expression::Parenthesis (Lex &l)
{
   Error Return = SQL_SUCCESS;
   PStack(StackItem) localStack;
   Token *t;

   Return = Item (l, localStack);
   if (Return == SQL_SUCCESS) {
      t = l.GetToken ();
      if (t == 0 || t->token != ')') {
         Return = SQL_ERROR;
         if (t)
            PrintError (E_SYNTAX_ERROR_000, "Missing parenthesis before " + t->word);
         else
            PrintError (E_SYNTAX_ERROR_000, "Missing parenthesis before end");
      } else {
         reduce(0, localStack);
      }
   }
   while (localStack.Size() > 0)
      delete localStack.Pop();
   return Return;
}

Error
Expression::Args (Lex &l, int nArgs)
{
   Error Return = SQL_SUCCESS;
   PStack(StackItem) localStack;
   Token *t;

   while (Return == SQL_SUCCESS && nArgs-- > 1) {
      Return = Item (l, localStack);
      if (Return == SQL_SUCCESS) {
         t = l.GetToken ();
         if (t == 0 || t->token != ',') {
            Return = SQL_ERROR;
            if (t)
               PrintError (E_SYNTAX_ERROR_000, "Missing comma before " + t->word);
            else
               PrintError (E_SYNTAX_ERROR_000, "Missing comma before end");
         } else {
            reduce(0, localStack);
         }
      }
      while (localStack.Size() > 0)
         delete localStack.Pop();
   }
   if (Return == SQL_SUCCESS)
      Return = Parenthesis (l);
   return Return;
}

Error
Expression::ConvertArgs (Lex &l)
{
   Error Return = SQL_SUCCESS;
   PStack(StackItem) localStack;
   Token *t;

   Return = Item (l, localStack);
   if (Return == SQL_SUCCESS) {
      t = l.GetToken ();
      if (t == 0 || t->token != ',') {
         Return = SQL_ERROR;
         if (t)
            PrintError (E_SYNTAX_ERROR_000, "Missing comma before " + t->word);
         else
            PrintError (E_SYNTAX_ERROR_000, "Missing comma before end");
      } else {
         reduce(0, localStack);
      }
   }

   while (localStack.Size() > 0)
      delete localStack.Pop();

   if (Return == SQL_SUCCESS) {
      t = l.GetToken ();
      if (t == 0 || t->token != USER_NAME) {
         Return = SQL_ERROR;
         if (t)
            PrintError (E_SYNTAX_ERROR_000, "Expected <data type>, found < " + t->word + ">");
         else
            PrintError (E_SYNTAX_ERROR_000, "Expected <data type>, found <end statement>");
      } else {
         if (t->word == "SQL_CHAR" || t->word == "CHAR")
            stack.AddItem(new StackItem (TO_CHAR));
         else if (t->word == "SQL_DOUBLE" || t->word == "DOUBLE")
            stack.AddItem(new StackItem (TO_DOUBLE));
         else {
            Return = SQL_ERROR;
            PrintError (E_SYNTAX_ERROR_000, "Unsupported data type < " + t->word + ">");
         }
      }
   }
   
   if (Return == SQL_SUCCESS) {
      t = l.GetToken ();
      if (t == 0 || t->token != ')') {
         Return = SQL_ERROR;
         if (t)
            PrintError (E_SYNTAX_ERROR_000, "Missing parenthesis before " + t->word);
         else
            PrintError (E_SYNTAX_ERROR_000, "Missing parenthesis before end");
      }
   }
   return Return;
}

Error
Expression::Item (Lex &l, PStack(StackItem) &localStack)
{
   toknum tmp;
   Error Return = SQL_SUCCESS;

   Token *t = l.GetToken();
   if (t) {
      switch (t->token) {
      case ABS:
      case ACOS:
      case ASIN:
      case ATAN:
      case CEILING:
      case COS:
      case EXP:
      case FLOOR:
      case LOG:
      case LOG10:
      case SIGN:
      case SIN:
      case SQRT:
      case TAN:
      case COT:
      case UPPER:
      case LOWER:
      case LCASE:
      case UCASE:
      case LTRIM:
      case RTRIM:
      case CURRENT_TIMESTAMP:
      case CURRENT_TIMESTAMP_28:
      case DAYOFMONTH:
      case HOUR:
      case MILLISECOND:
      case MINUTE:
      case MONTH:
      case SECOND:
      case YEAR:
      case LENGTH:
      case ASCII:
      case CHAR_:
      case SPACE:
         tmp = t->token;
         t = l.GetToken();
         if (t == 0) {
            PrintError (E_SYNTAX_ERROR_000, "expected '(' found: NULL ");
            Return = SQL_ERROR;
            break;
         } else if (t->token != '(') {
            PrintError (E_SYNTAX_ERROR_000, "expected '(' found: '" + t->word + "'");
            Return = SQL_ERROR;
            break;
         } else {
            Return = Args (l, 1);;
            if (Return == SQL_SUCCESS) {
               stack.AddItem(new StackItem (tmp));
               Return = ArithmOp (l, localStack);
            }
         }
         break;
      case CONVERT:
         t = l.GetToken();
         if (t == 0) {
            PrintError (E_SYNTAX_ERROR_000, "expected '(' found: NULL ");
            Return = SQL_ERROR;
            break;
         } else if (t->token != '(') {
            PrintError (E_SYNTAX_ERROR_000, "expected '(' found: '" + t->word + "'");
            Return = SQL_ERROR;
            break;
         } else {
            Return = ConvertArgs (l);
            if (Return == SQL_SUCCESS)
               Return = ArithmOp (l, localStack);
         }
         break;
      case ROUND:
      case TRUNCATE:
      case CONCAT:
         tmp = t->token;
         t = l.GetToken();
         if (t == 0) {
            PrintError (E_SYNTAX_ERROR_000, "expected '(' found: NULL ");
            Return = SQL_ERROR;
            break;
         } else if (t->token != '(') {
            PrintError (E_SYNTAX_ERROR_000, "expected '(' found: '" + t->word + "'");
            Return = SQL_ERROR;
            break;
         } else {
            Return = Args (l, 2);
            if (Return == SQL_SUCCESS) {
               stack.AddItem(new StackItem (tmp));
               Return = ArithmOp (l, localStack);
            }
         }
         break;
      case SUBSTRING:
      case LOCATE:
         tmp = t->token;
         t = l.GetToken();
         if (t == 0) {
            PrintError (E_SYNTAX_ERROR_000, "expected '(' found: NULL ");
            Return = SQL_ERROR;
            break;
         } else if (t->token != '(') {
            PrintError (E_SYNTAX_ERROR_000, "expected '(' found: '" + t->word + "'");
            Return = SQL_ERROR;
            break;
         } else {
            Return = Args (l, 3);
            if (Return == SQL_SUCCESS) {
               stack.AddItem(new StackItem (tmp));
               Return = ArithmOp (l, localStack);
            }
         }
         break;
      case '(':
         Return = Parenthesis(l);
         if (Return == SQL_SUCCESS)
            Return = ArithmOp (l, localStack);
         break;
      case '+':
         Return = Item (l, localStack);
         break;
      case '-':
         {
            Token tk;
            tk.word = "-1";
            tk.token = NUMBER;
            localStack.Push(new StackItem(*(parent->GetDB()), tk));
            localStack.Push(new StackItem((toknum) '*'));
            Return = Item (l, localStack);

            if (Return == SQL_SUCCESS)
               Return = ArithmOp (l, localStack);
         }
         break;
      case NUMBER:
         localStack.Push(new StackItem(*(parent->GetDB()), *t));
         Return = ArithmOp (l, localStack);
         break;
      case USER_NAME:
         {
            StmtAttr *sa = UserName (l, t);
            if (sa) {
               localStack.Push(new StackItem(sa));
               Return = ArithmOp (l, localStack);
            } else {
               Return = SQL_ERROR;
            }
         }
         break;
      /* Non Supportato
      case COUNT:
      case AVG:
      case SUM:
      case MAX:
      case MIN:
         if (parseAggregate) {
            StmtAttr *sa = DistinctFunction (l);
            if (sa) {
               localStack.Push(new StackItem(sa));
               sa = new StmtAttr (*sa);
               sa->setBuiltIn (t->token);
               DistField.AddItem (sa);
               if (sa->getAttr()->IsNumeric())
                  Return = ArithmOp (l, localStack);
               else
                  Return = SQL_SUCCESS;
            } else
               Return = SQL_ERROR;
         } else {
            PrintError (E_SYNTAX_ERROR_000, "Aggregate function not allowed in that context");
            Return = SQL_ERROR;
         }
         break;
      */
      case '?':
         localStack.Push(new StackItem(&(parent->NewParam()->GetParamAttr()), PTrue));
         Return = ArithmOp (l, localStack);
         break;
      case STR_LITERAL:
      case TIMESTAMP:
         localStack.Push(new StackItem(*(parent->GetDB()), *t));
         break;
      default:
         Return = SQL_ERROR;
         PrintError (E_SYNTAX_ERROR_000, "Invalid token " + t->word);
         break;
      }
   } else {
      Return = SQL_ERROR;
      PrintError (E_SYNTAX_ERROR_000, "Unaspected end of statement");
   }
   return Return;
}


StmtAttr *
Expression::UserName (Lex &l, Token *t)
{
   StmtAttr *Return = 0;
   PString  *fieldName = &t->word, tableName;
   StmtRel *sr;
   t = l.GetToken();
   PBool ok = PTrue;
   if (t) {
      if (t->token == '.') {
         t = l.GetToken();
         if (t && t->token == USER_NAME) {
            tableName = *fieldName;
            fieldName = &t->word;
         } else {
            ok = PFalse;
            if (t)
               PrintError (E_SYNTAX_ERROR_000, "Invalid token near " + t->word);
            else
               PrintError (E_SYNTAX_ERROR_000, "Unexpected end of line");
         }
      } else {
         l.UngetToken();
      }
   }
   if (ok)
      Return = (parent->*findAttr) (*fieldName, tableName, &sr, aggregate);
   return Return;
}

StmtAttr *
Expression::DistinctFunction (Lex &l)
{
   StmtAttr *Return = 0;


   Token *t = l.GetToken();
   if (t && t->token == '(') {
      t = l.GetToken();
      if (t && t->token == DISTINCT) {
         t = l.GetToken();
         if (t && t->token == USER_NAME) {
            Return = UserName (l, t);
            if (Return) {
               if ((aggregate == AVG || aggregate == SUM) &&
                   !Return->getAttr()->IsNumeric()) {
                  PrintError (E_SYNTAX_ERROR_000, "Not numeric argument for distinct function");
                  Return = 0;
               } else {
                  t = l.GetToken();
                  if (t == 0 || t->token != (toknum)')') {
                     if (t)
                        PrintError (E_SYNTAX_ERROR_000, "Expected <)> found " + t->word + ">");
                     else
                        PrintError (E_SYNTAX_ERROR_000, "Expected <)> found <end of statement>");
                     Return = 0;
                  }
               }
            }
         } else {
            if (t)
               PrintError (E_SYNTAX_ERROR_000, "Expected <column name> found " + t->word + ">");
            else
               PrintError (E_SYNTAX_ERROR_000, "Expected <column name> found <end of statement>");
         }
      } else {
         if (t)
            PrintError (E_SYNTAX_ERROR_000, "Expected <DISTINCT> found " + t->word + ">");
         else
            PrintError (E_SYNTAX_ERROR_000, "Expected <DISTINCT> found <end of statement>");
      }
   } else {
      if (t)
         PrintError (E_SYNTAX_ERROR_000, "Expected <(> found " + t->word + ">");
      else
         PrintError (E_SYNTAX_ERROR_000, "Expected <(> found <end of statement>");
   }
   return Return;
}

void
Expression::reduce (Token *lookAhead, PStack(StackItem) &localStack)
{
   StackItem *leftItem, *opItem, *rightItem;
   PBool goOn = PTrue;
 
   rightItem = localStack.Pop();
   while (rightItem != 0 && goOn) {
      goOn = PFalse;
      if (rightItem->sa != 0) {  // c'e' un attributo 
         opItem = localStack.Pop();
         if (opItem && opItem->sa == 0) {  // c'e' un operatore 
            if (lookAhead && 
                (lookAhead->token == '*' || lookAhead->token == '/') &&
                (opItem->type == '-' || opItem->type == '+')) {
               localStack.Push (opItem);
               localStack.Push (rightItem);
            } else {
               leftItem = localStack.Pop();
               if (leftItem && leftItem->sa != 0) { // c'e' il secondo attributo
                  stack.AddItem (leftItem);
                  stack.AddItem (rightItem);
                  stack.AddItem (opItem);
                  rightItem = localStack.Pop();
                  goOn = PTrue;
               } else {   // non c'e' il secondo attributo
                  if (leftItem != 0)
                     localStack.Push (leftItem);
                  if (stack.GetItemNum() > 0) {
                     stack.AddItem (rightItem);
                     stack.AddItem (opItem);
                  } else {
                     PrintError (E_SYNTAX_ERROR_000, "Invalid production left");
                  }
               }
            }
         } else {// c'e' solo un attributo
            if (opItem != 0)
               localStack.Push (opItem);
            if (lookAhead == 0 || lookAhead->token == ')')
               stack.AddItem (rightItem);
            else
               localStack.Push (rightItem);
         }
      } else { // il primo termine non e' un attributo
         opItem = rightItem;
         if (stack.GetItemNum() > 0) { // nella pila ci sono gia' elementi
            if (lookAhead && 
                (lookAhead->token == '*' || lookAhead->token == '/') &&
                (opItem->type == '-' || opItem->type == '+')) {
               localStack.Push(opItem);
            } else {
               leftItem = localStack.Pop();
               if (leftItem && leftItem->sa != 0) { // c'e' il secondo attributo
                  opItem->type = (toknum)(opItem->type|0x80);
                  stack.AddItem (leftItem);
                  stack.AddItem (opItem);
                  rightItem = localStack.Pop();
                  goOn = PTrue;
               } else {
                  if (leftItem != 0)
                     localStack.Push (leftItem);
                  stack.AddItem (opItem);
               }
            }
         } else {
            PrintError (E_SYNTAX_ERROR_000, "Invalid production right");
         }
      }
   }
}

Error
Expression::ArithmOp (Lex &l, PStack(StackItem) &localStack)
{
   Error Return = SQL_SUCCESS;
   Token *t = l.GetToken();

   reduce (t, localStack);

   if (t) {
      switch (t->token) {
      case '+':
      case '-':
      case '*':
      case '/':
         localStack.Push(new StackItem(t->token));
         Return = Item (l, localStack);
         break;
      default:
         l.UngetToken();
         Return = Alias (l);
         break;
      }
   }
   return Return;
}

Error
Expression::Alias (Lex &l)
{
   Error Return = SQL_SUCCESS;
   Token *t = l.GetToken();

   if (t) {
      switch (t->token) {
      case AS:
         t = l.GetToken();
         if (t && (t->token == USER_NAME || t->token == STR_LITERAL))
           ; /* PASSTHRU */
         else {
            Return = SQL_ERROR;
            if (t)
               PrintError (E_SYNTAX_ERROR_000, "Invalid token near " + t->word);
            else
               PrintError (E_SYNTAX_ERROR_000, "Invalid token near end");
            break;
         }
      case USER_NAME:
      case STR_LITERAL:
         if (t->token == STR_LITERAL)
            name = t->word (1, t->word.size() - 2);
         else
            name = t->word;
         break;
      default:
         l.UngetToken();
         break;
      }
   }
   return Return;
}

extern "C" {
# include <string.h>
# include <ctype.h>
};
class ExprItem {
   enum {
      NUMBER,
      STRING,
      UNKNOWN
   } type;
   char *str;
   double num;
   long len;
   const char *dateFormat;
public:
   ExprItem () {
      type = UNKNOWN;
      str = 0;
      num = 0;
      len = SQL_NULL_DATA;
      dateFormat = 0;
   }
   ExprItem (ExprItem & e) {
      *this = e;
   }
   ~ExprItem () {
      clear();
   }
   void clear() {
      if (str)
         delete str;
      str = 0;
      num = NULLDOUBLE;
      type = UNKNOWN;
   }
   void operator=(ExprItem &e){
      clear();
      type = e.type;
      dateFormat = e.dateFormat;
      if (e.type == STRING) {
         str = e.str;
         len = e.len;
         e.len = SQL_NULL_DATA;
         e.str = 0;
      } else
         num = e.num;
   }
   void set (char *s, long l, const char *t = 0) {
      clear();
      type = STRING;
      len = l;
      dateFormat = t;
      if (l >= 0) {
         str = new char[len + 1];
         strncpy (str, s, (int)len);
         str[len] = 0;
      }
   }
   
   void setStr (char *s, long l) {
      clear();
      type = STRING;
      len = l;
      str = s;
   }
   void set (const double & d) {
      clear();
      type = NUMBER;
      num = d;
   }
   double & getNum () {
      return num;
   }
   char * getStr () {
      return str;
   }
   const long & getLen () {
      return len;
   }
   PBool isString () {
      return type == STRING || type == UNKNOWN;
   }
   PBool isNumber () {
      return type == NUMBER || type == UNKNOWN;
   }
   PBool isDate () {
      return dateFormat != 0;
   }
   const char * getDateFmt() {
      return dateFormat;
   }
};

class ExprItemSStack : public PObject {
   ExprItem ** array;
   int numItems;
   int max;
public:
   void Push (ExprItem & s)    { *(array[numItems++]) = s; }
   void Push (char *s, long l, const char *df = 0) {
      array[numItems++]->set(s, l, df);
   }
   void Push (double d)    { array[numItems++]->set(d); }
   void Push ()    { array[numItems++]->clear(); }
   ExprItem & Pop () { return *(array[--numItems]); }
   int Size () {  return numItems; }
   ExprItemSStack(int n) {
      max = n;
      numItems = 0;
      if (n > 0) {
         //array = new ExprItem[n];
         array = new ExprItem * [n];
         while (--n >= 0)
            array[n] = new ExprItem;
      }
   }
   ~ExprItemSStack() {
      if (max > 0) {
         while (--max >= 0)
            delete array[max];
         delete array;
      }
   }
};

inline PBool
checkArgs (ExprItemSStack & s, char op, int n)
{
   if (s.Size() < n) {
      PString p;
      p = "Corrupted expression near '";
      p += op + "'";
      PError::InternalError (p.gets());
      return PFalse;
   } else
      return PTrue;
}

void
Expression::toString (PString &result)
{
   StackItem *item;
   PString *s1, *s2, *s3;
   
   if (stack.GetItemNum() == 1) {
      item = stack.GetFirst();
      if (item->sa != 0) {
         if (item->sa->GetStmtParent()) {
            result += item->sa->GetStmtParent()->getName();
            result += '.';
            result += item->sa->getName();
         } else if (item->sa->getAttr()->IsNumeric()) {
            result += item->sa->getName();
         } else {
            result += "'";
            result += item->sa->getName();
            result += "'";
         }
      }
   } else {
      PStack(PString) ss(stack.GetItemNum());
      for (item = stack.GetFirst(); item != 0; item = stack.GetNext()) {
         switch (item->type) {
         case '*':
         case '*'|0x80:
            s1 = ss.Pop();
            s2 = ss.Pop();
            ss.Push(new PString (*s2 + (char) item->type + *s1));
            delete s1;
            delete s2;
            break;
         case '/':
            s1 = ss.Pop();
            s2 = ss.Pop();
            ss.Push(new PString (*s2 + (char) item->type + *s1));
            delete s1;
            delete s2;
            break;
         case '/'|0x80:
            s1 = ss.Pop();
            s2 = ss.Pop();
            ss.Push(new PString (*s1 + (char) item->type + *s2));
            delete s1;
            delete s2;
            break;
         case '+'|0x80:
         case '+':
            s1 = ss.Pop();
            s2 = ss.Pop();
            ss.Push(new PString ("(" + *s2 + (char) item->type + *s1 + ")"));
            delete s1;
            delete s2;
            break;
         case '-':
            s1 = ss.Pop();
            s2 = ss.Pop();
            ss.Push(new PString ("(" + *s2 + (char) item->type + *s1 + ")"));
            delete s1;
            delete s2;
            break;
         case '-'|0x80:
            s1 = ss.Pop();
            s2 = ss.Pop();
            ss.Push(new PString ("(" + *s1 + (char) item->type + *s2 + ")"));
            delete s1;
            delete s2;
            break;
         break;
         case ACOS:
            s1 = ss.Pop();
            ss.Push(new PString ("ACOS(" + *s1 + ")"));
            delete s1;
            break;
         case ASIN:
            s1 = ss.Pop();
            ss.Push(new PString ("ASIN(" + *s1 + ")"));
            delete s1;
            break;
         case ATAN:
            s1 = ss.Pop();
            ss.Push(new PString ("ATAN(" + *s1 + ")"));
            delete s1;
            break;
         case CEILING:
            s1 = ss.Pop();
            ss.Push(new PString ("CEILING(" + *s1 + ")"));
            delete s1;
            break;
         case COS:
            s1 = ss.Pop();
            ss.Push(new PString ("COS(" + *s1 + ")"));
            delete s1;
            break;
         case EXP:
            s1 = ss.Pop();
            ss.Push(new PString ("EXP(" + *s1 + ")"));
            delete s1;
            break;
         case FLOOR:
            s1 = ss.Pop();
            ss.Push(new PString ("FLOOR(" + *s1 + ")"));
            delete s1;
            break;
         case LOG:
            s1 = ss.Pop();
            ss.Push(new PString ("LOG(" + *s1 + ")"));
            delete s1;
            break;
         case LOG10:
            s1 = ss.Pop();
            ss.Push(new PString ("LOG10(" + *s1 + ")"));
            delete s1;
            break;
         case SIGN:
            s1 = ss.Pop();
            ss.Push(new PString ("SIGN(" + *s1 + ")"));
            delete s1;
            break;
         case SIN:
            s1 = ss.Pop();
            ss.Push(new PString ("SIN(" + *s1 + ")"));
            delete s1;
            break;
         case SQRT:
            s1 = ss.Pop();
            ss.Push(new PString ("SQRT(" + *s1 + ")"));
            delete s1;
            break;
         case TAN:
            s1 = ss.Pop();
            ss.Push(new PString ("TAN(" + *s1 + ")"));
            delete s1;
            break;
         case COT:
            s1 = ss.Pop();
            ss.Push(new PString ("COT(" + *s1 + ")"));
            delete s1;
            break;
         case ABS:
            s1 = ss.Pop();
            ss.Push(new PString ("ABS(" + *s1 + ")"));
            delete s1;
            break;
         case TO_CHAR:
            s1 = ss.Pop();
            ss.Push(new PString ("CONVERT(" + *s1 + ", CHAR)"));
            delete s1;
            break;
         case TO_DOUBLE:
            s1 = ss.Pop();
            ss.Push(new PString ("CONVERT(" + *s1 + ", DOUBLE)"));
            delete s1;
            break;
         case LOWER: // Un argomento stringa, risultato stringa
         case LCASE:
            s1 = ss.Pop();
            ss.Push(new PString ("LOWER(" + *s1 + ")"));
            delete s1;
            break;
         case UPPER:
         case UCASE:
            s1 = ss.Pop();
            ss.Push(new PString ("UPPER(" + *s1 + ")"));
            delete s1;
            break;
         case RTRIM:
            s1 = ss.Pop();
            ss.Push(new PString ("RTRIM(" + *s1 + ")"));
            delete s1;
            break;
         case LTRIM:
            s1 = ss.Pop();
            ss.Push(new PString ("LTRIM(" + *s1 + ")"));
            delete s1;
            break;
         case CURRENT_TIMESTAMP:
            s1 = ss.Pop();
            ss.Push(new PString ("CURRENT_TIMESTAMP(" + *s1 + ")"));
            delete s1;
            break;
         case CURRENT_TIMESTAMP_28:
            s1 = ss.Pop();
            ss.Push(new PString ("CURRENT_TIMESTAMP_28(" + *s1 + ")"));
            delete s1;
            break;
         case DAYOFMONTH:
            s1 = ss.Pop();
            ss.Push(new PString ("DAYOFMONTH(" + *s1 + ")"));
            delete s1;
            break;
         case HOUR:
            s1 = ss.Pop();
            ss.Push(new PString ("HOUR(" + *s1 + ")"));
            delete s1;
            break;
         case MILLISECOND:
            s1 = ss.Pop();
            ss.Push(new PString ("MILLISECOND(" + *s1 + ")"));
            delete s1;
            break;
         case MINUTE:
            s1 = ss.Pop();
            ss.Push(new PString ("MINUTE(" + *s1 + ")"));
            delete s1;
            break;
         case MONTH:
            s1 = ss.Pop();
            ss.Push(new PString ("MONTH(" + *s1 + ")"));
            delete s1;
            break;
         case SECOND:
            s1 = ss.Pop();
            ss.Push(new PString ("SECOND(" + *s1 + ")"));
            delete s1;
            break;
         case YEAR:
            s1 = ss.Pop();
            ss.Push(new PString ("YEAR(" + *s1 + ")"));
            delete s1;
            break;
         case LENGTH:
            s1 = ss.Pop();
            ss.Push(new PString ("LENGTH(" + *s1 + ")"));
            delete s1;
            break;
         case ASCII:
            s1 = ss.Pop();
            ss.Push(new PString ("ASCII(" + *s1 + ")"));
            delete s1;
            break;
         case CHAR_:
            s1 = ss.Pop();
            ss.Push(new PString ("CHAR(" + *s1 + ")"));
            delete s1;
            break;
         case SPACE:
            s1 = ss.Pop();
            ss.Push(new PString ("SPACE(" + *s1 + ")"));
            delete s1;
            break;
         case CONCAT:
            s1 = ss.Pop();
            s2 = ss.Pop();
            ss.Push(new PString ("CONCAT(" + *s2 + "," + *s1 + ")"));
            delete s1;
            delete s2;
            break;
         case ROUND:
            s1 = ss.Pop();
            s2 = ss.Pop();
            s3 = new PString ("ROUND(" + *s2 + "," + *s1 + ")");
            ss.Push(s3);
            delete s1;
            delete s2;
            break;
         case TRUNCATE:
            s1 = ss.Pop();
            s2 = ss.Pop();
            s3 = new PString ("TRUNCATE(" + *s2 + "," + *s1 + ")");
            ss.Push(s3);
            delete s1;
            delete s2;
            break;
         case SUBSTRING:
            s1 = ss.Pop();
            s2 = ss.Pop();
            s3 = ss.Pop();
            ss.Push(new PString ("SUBSTRING(" + *s3 + "," + *s2 + "," + *s1 + ")"));
            delete s1;
            delete s2;
            delete s3;
            break;
         case LOCATE:
            s1 = ss.Pop();
            s2 = ss.Pop();
            s3 = ss.Pop();
            ss.Push(new PString ("LOCATE(" + *s3 + "," + *s2 + "," + *s1 + ")"));
            delete s1;
            delete s2;
            delete s3;
            break;
         default:
            s1 = new PString();
            if (item->sa != 0) {
               if (item->sa->GetStmtParent()) {
                  *s1 += item->sa->GetStmtParent()->getName();
                  *s1 += '.';
               }
               *s1 += item->sa->getName();
            }
            ss.Push(s1);
            break;
         }
      }
      s1 = ss.Pop();
      result = *s1;
      delete s1;
   }
}

Error
Expression::verify(int & numeric, PBool isFormal)
{
   ExprItemSStack rpnStack ((int)stack.GetItemNum());
   ExprItem b1, b2, b3;
   StackItem *item;

   
   for (item = stack.GetFirst(); item != 0; item = stack.GetNext())
      switch (item->type) {
      case '*'|0x80: // Due argomenti numerici, risultato numerico
      case '*':
      case '/':
      case '/'|0x80:
      case '+'|0x80:
      case '+':
      case '-':
      case '-'|0x80:
         if (checkArgs (rpnStack, item->type, 2)) {
            b1 = rpnStack.Pop();
            b2 = rpnStack.Pop();
            if (!b1.isNumber() || !b2.isNumber()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid operands in arithmetic expression");
               return SQL_ERROR;
            } else {
               rpnStack.Push(1.0);
            }
         }
         break;
      case ABS: // Un argomento numerico, risultato numerico
      case ACOS:
      case ASIN:
      case ATAN:
      case CEILING:
      case COS:
      case EXP:
      case FLOOR:
      case LOG:
      case LOG10:
      case SIGN:
      case SIN:
      case SQRT:
      case TAN:
      case COT:
         if (checkArgs (rpnStack, item->type, 1)) {
            b1 = rpnStack.Pop();
            if (!b1.isNumber()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid argument in function");
               return SQL_ERROR;
            } else {
               rpnStack.Push(1.0);
            }
         }
         break;
      case ROUND: // Due argomenti numerici, risultato numerico
      case TRUNCATE: 
         if (checkArgs (rpnStack, item->type, 2)) {
            b1 = rpnStack.Pop();
            b2 = rpnStack.Pop();
            if (!b1.isNumber() || !b2.isNumber()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid argument in ROUND/TRUNCATE");
               return SQL_ERROR;
            } else {
               rpnStack.Push(1.0);
            }
         }
         break;
      case TO_CHAR: // Un argomento qualsiasi, risultato stringa
         if (checkArgs (rpnStack, item->type, 1)) {
            b1 = rpnStack.Pop();
            rpnStack.Push("", 0);
         }
         break;
      case TO_DOUBLE: // Un argomento qualsiasi, risultato numerico
         if (checkArgs (rpnStack, item->type, 1)) {
            b1 = rpnStack.Pop();
            rpnStack.Push(1.0);
         }
         break;
      case LOWER: // Un argomento stringa, risultato stringa
      case UPPER:
      case LCASE:
      case UCASE:
      case RTRIM:
      case LTRIM:
      case CURRENT_TIMESTAMP:
      case CURRENT_TIMESTAMP_28:
         if (checkArgs (rpnStack, item->type, 1)) {
            b1 = rpnStack.Pop();
            if (!b1.isString()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid argument in string function");
               return SQL_ERROR;
            } else {
               rpnStack.Push("",0);
            }
         }
         break;
      case DAYOFMONTH: // Un argomento timestamp, risultato numerico
      case HOUR:
      case MILLISECOND:
      case MINUTE:
      case MONTH:
      case SECOND:
      case YEAR:
         if (checkArgs (rpnStack, item->type, 1)) {
            b1 = rpnStack.Pop();
            if (!b1.isDate()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid argument in string function");
               return SQL_ERROR;
            } else {
               rpnStack.Push(1.0);
            }
         }
         break;
      case CHAR_:
      case SPACE: // Un argomento numerico, risultato stringa
         if (checkArgs (rpnStack, item->type, 1)) {
            b1 = rpnStack.Pop();
            if (b1.isString()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid argument in string function");
               return SQL_ERROR;
            } else {
               rpnStack.Push("",0);
            }
         }
         break;
      case LENGTH: // Un argomento stringa, risultato numerico
      case ASCII:
         if (checkArgs (rpnStack, item->type, 1)) {
            b1 = rpnStack.Pop();
            if (!b1.isString()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid arguments in string function");
               return SQL_ERROR;
            } else {
               rpnStack.Push(1.0);
            }
         }
         break;
      case CONCAT: // Due argomenti stringa, risultato stringa
         if (checkArgs (rpnStack, item->type, 2)) {
            b1 = rpnStack.Pop();
            b2 = rpnStack.Pop();
            if (!b1.isString() || !b2.isString()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid operands in string function");
               return SQL_ERROR;
            } else {
               rpnStack.Push("", 0);
            }
         }
         break;
      case SUBSTRING: // Un argomento stringa e due numerici, risultato stringa
         if (checkArgs (rpnStack, item->type, 3)) {
            b3 = rpnStack.Pop();
            b2 = rpnStack.Pop();
            b1 = rpnStack.Pop();
            if (!b1.isString() || !b2.isNumber() || !b3.isNumber()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid operands in string function");
               return SQL_ERROR;
            } else {
               rpnStack.Push("", 0);
            }
         }
         break;
      case LOCATE: // Due argomenti stringa e uno numerico, risultato numerico
         if (checkArgs (rpnStack, item->type, 3)) {
            b3 = rpnStack.Pop();
            b2 = rpnStack.Pop();
            b1 = rpnStack.Pop();
            if (!b1.isString() || !b2.isString() || !b3.isNumber()) {
               PrintError (E_SYNTAX_ERROR_000, "Invalid operands in string function");
               return SQL_ERROR;
            } else {
               rpnStack.Push(1.0);
            }
         }
         break;
      default:
         if (item->sa != 0) {
            if (item->isParam && isFormal)
               rpnStack.Push ();
            else if (item->sa->getAttr()->IsNumeric())
               rpnStack.Push (1.0);
            else
               rpnStack.Push ("", 0, item->sa->getAttr()->getDateFmt());
         } else { 
            PString p;
            p = "Corrupted expression near '";
            p += (char)(item->type) + "'";
            PError::InternalError (p.gets());
            return SQL_ERROR;
         }
         break;
      }

   if (rpnStack.Size() != 1) {
      PrintError (E_UNKNOWN_ERROR, "Corrupted expression stack");
      return SQL_ERROR;
   }
   b1 = rpnStack.Pop();
   if (b1.isString())
      numeric = PFalse;
   else
      numeric = PTrue;
   return SQL_SUCCESS;
}

/* start funzioni */
static double
doubleRound (double num, int dec)
{
   int negative = dec < 0;
   double factor = 1;
   double Return;
   if (negative)
      dec = -dec;
   for (; dec > 0; dec--)
      factor *= 10;
   if (negative)
      num /= factor;
   else
      num *= factor;
   Return = floor (num);
   if (num - Return >= 0.5)
      Return++;
   if (negative)
      Return *= factor;
   else
      Return /= factor;
   return Return;
}


static double
doubleTruncate (double num, int dec)
{
   int negative = dec < 0;
   double factor = 1;
   double Return;
   if (negative)
      dec = -dec;
   for (; dec > 0; dec--)
      factor *= 10;
   if (negative)
      num /= factor;
   else
      num *= factor;
   Return = floor (num);
   if (negative)
      Return *= factor;
   else
      Return /= factor;
   return Return;
}
/* end funzioni */

void
Expression::Eval ()
{
   ExprItemSStack rpnStack ((int)stack.GetItemNum());
   double dbl = 0;
   ExprItem b1, b2, b3;
   StackItem *item;
   char *str;
   long len;
   register int i;

   
   for (item = stack.GetFirst(); item != 0; item = stack.GetNext())
      switch (item->type) { // Tutti gli operatori debbono essere anche in verify
      case '*'|0x80:
      case '*':
         b1 = rpnStack.Pop();
         b2 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else if (b2.getNum() == NULLDOUBLE)
            rpnStack.Push (b2);
         else
            rpnStack.Push (b2.getNum() * b1.getNum());
         break;
      case '/':
         b1 = rpnStack.Pop();
         b2 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else if (b2.getNum() == NULLDOUBLE)
            rpnStack.Push (b2);
         else
            rpnStack.Push (b2.getNum() / b1.getNum());
         break;
      case '/'|0x80:
         b1 = rpnStack.Pop();
         b2 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else if (b2.getNum() == NULLDOUBLE)
            rpnStack.Push (b2);
         else
            rpnStack.Push (b1.getNum() / b2.getNum());
         break;
      case '+'|0x80:
      case '+':
         b1 = rpnStack.Pop();
         b2 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else if (b2.getNum() == NULLDOUBLE)
            rpnStack.Push (b2);
         else
            rpnStack.Push (b2.getNum() + b1.getNum());
         break;
      case '-':
         b1 = rpnStack.Pop();
         b2 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else if (b2.getNum() == NULLDOUBLE)
            rpnStack.Push (b2);
         else
            rpnStack.Push (b2.getNum() - b1.getNum());
         break;
      case '-'|0x80:
         b1 = rpnStack.Pop();
         b2 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else if (b2.getNum() == NULLDOUBLE)
            rpnStack.Push (b2);
         else
            rpnStack.Push (b1.getNum() - b2.getNum());
         break;
      case ACOS:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (acos(b1.getNum()));
         break;
      case ASIN:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (asin(b1.getNum()));
         break;
      case ATAN:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (atan(b1.getNum()));
         break;
      case CEILING:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (ceil(b1.getNum()));
         break;
      case COS:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (cos(b1.getNum()));
         break;
      case EXP:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (exp(b1.getNum()));
         break;
      case FLOOR:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (floor(b1.getNum()));
         break;
      case LOG:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (log(b1.getNum()));
         break;
      case LOG10:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (log10(b1.getNum()));
         break;
      case SIGN:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (b1.getNum() < 0 ? -1.0 : (b1.getNum() > 0 ? 1.0 : 0));
         break;
      case SIN:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (sin(b1.getNum()));
         break;
      case SQRT:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (sqrt(b1.getNum()));
         break;
      case TAN:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (tan(b1.getNum()));
         break;
      case COT:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (1.0/tan(b1.getNum()));
         break;
      case ABS:
         b1 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else 
            rpnStack.Push (b1.getNum() < 0 ? -b1.getNum() : b1.getNum());
         break;
      case ROUND:
         b1 = rpnStack.Pop();
         b2 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else if (b2.getNum() == NULLDOUBLE)
            rpnStack.Push (b2);
         else 
            rpnStack.Push (doubleRound (b2.getNum(), (int)b1.getNum()));
         break;
      case TRUNCATE:
         b1 = rpnStack.Pop();
         b2 = rpnStack.Pop();
         if (b1.getNum() == NULLDOUBLE)
            rpnStack.Push (b1);
         else if (b2.getNum() == NULLDOUBLE)
            rpnStack.Push (b2);
         else 
            rpnStack.Push (doubleTruncate (b2.getNum(), (int)b1.getNum()));
         break;
      case TO_CHAR:
         b1 = rpnStack.Pop();
         if (b1.isNumber()) {
            str = new char[48];
            sprintf (str, "%.36g", b1.getNum());
            b1.setStr(str, strlen(str));
            rpnStack.Push (b1);
         } else
            rpnStack.Push (b1);
         break;
      case TO_DOUBLE:
         b1 = rpnStack.Pop();
         if (b1.isString()) {
            rpnStack.Push (atof (b1.getStr()));
         } else
            rpnStack.Push (b1);
         break;
      case LOWER: //Inizio funzioni stringa
      case LCASE:
         b1 = rpnStack.Pop();
         for (i = 0; i < b1.getLen(); i++)
            b1.getStr()[i] = tolower(b1.getStr()[i]);
         rpnStack.Push (b1);
         break;
      case UPPER:
      case UCASE:
         b1 = rpnStack.Pop();
         for (i = 0; i < b1.getLen(); i++)
            b1.getStr()[i] = toupper(b1.getStr()[i]);
         rpnStack.Push (b1);
         break;
      case CONCAT:
         b2 = rpnStack.Pop();
         b1 = rpnStack.Pop();
         len = b1.getLen() + b2.getLen();
         str = new char[len + 1];
         strncpy (str, b1.getStr(), b1.getLen());
         str[b1.getLen()] = '\0';
         strncat (str, b2.getStr(), b2.getLen());
         str[len] = '\0';
         b1.setStr(str, len);
         rpnStack.Push (b1);
         break;
      case RTRIM:
         b1 = rpnStack.Pop();
         for (i = b1.getLen() - 1; i >= 0 && b1.getStr()[i] == ' '; i--)
            ;
         i++;
         b1.getStr()[i] = '\0';
         rpnStack.Push (b1);
         break;
      case LTRIM:
         b1 = rpnStack.Pop();
         for (i = 0; i < b1.getLen() && b1.getStr()[i] == ' '; i++)
            ;
         rpnStack.Push (&b1.getStr()[i], b1.getLen() - i);
         break;
      case CURRENT_TIMESTAMP:
         b1 = rpnStack.Pop();
         str = CurrentTime (parent->getCurrentTimestamp (),
                            b1.getStr(), parent->GetDB()->GetStartDate(), 0);
		   b1.setStr (str, strlen(str));
         rpnStack.Push (b1);
		   break;
      case CURRENT_TIMESTAMP_28:
         b1 = rpnStack.Pop();
         str = CurrentTime (parent->getCurrentTimestamp (), b1.getStr(),
                            parent->GetDB()->GetStartDate(), -28);
		   b1.setStr (str, strlen(str));
         rpnStack.Push (b1);
		   break;
      case DAYOFMONTH:
      case HOUR:
      case MILLISECOND:
      case MINUTE:
      case MONTH:
      case SECOND:
      case YEAR:
         b1 = rpnStack.Pop();
         rpnStack.Push (Extract (b1.getStr(), item->type, b1.getDateFmt(),
                                          parent->GetDB()->GetStartDate()));
		   break;
      case LENGTH:
         b1 = rpnStack.Pop();
         rpnStack.Push ((double)b1.getLen());
         break;
      case ASCII:
         b1 = rpnStack.Pop();
         rpnStack.Push ((double)b1.getStr()[0]);
         break;
      case CHAR_:
         {
            char c;
            b1 = rpnStack.Pop();
            c = (unsigned char) b1.getNum();
            rpnStack.Push (&c, 1);
         }
         break;
      case SPACE:
         {
            char *c;
            b1 = rpnStack.Pop();
            c = new char[(int) b1.getNum()];
            memset (c, ' ', (int) b1.getNum());
            rpnStack.Push (c, (int) b1.getNum());
            delete c;
         }
         break;
      case SUBSTRING:
         b3 = rpnStack.Pop();
         b2 = rpnStack.Pop();
         b1 = rpnStack.Pop();
         i = (int) (b2.getNum() < 1 ? 0 : b2.getNum() - 1);
         i = i > b1.getLen() ? b1.getLen() : i;
         len = (long) (b3.getNum() + i > b1.getLen() ? 
                       b1.getLen() - i : b3.getNum());
         len = len < 0 ? 0 : len;
         rpnStack.Push (&b1.getStr()[i], len);
         break;
      case LOCATE:
         b3 = rpnStack.Pop();
         b2 = rpnStack.Pop();
         b1 = rpnStack.Pop();
         i = (int) (b3.getNum() < 1 ? 0 : b3.getNum() - 1);
         i = i > b2.getLen() ? b2.getLen() : i;
         if ((str = strstr (&b2.getStr()[i], b1.getStr())) != 0)
            rpnStack.Push ((double) (str - b2.getStr() + 1));
         else
            rpnStack.Push (0.0);
         break;
      default:
         if (item->sa != 0) {
            if (item->sa->getAttr()->IsNumeric()) {
               item->sa->ToDouble (dbl, len);
               rpnStack.Push (dbl);
            } else {
               if (item->sa->isNull())
                  rpnStack.Push ("", 0);
               else if (item->sa->getAttr()->getDateType() != T_NODATE) {
                  long len;
                  char *out;
                  item->sa->ToChar (&out, len);
                  rpnStack.Push (out,len,item->sa->getAttr()->getDateFmt());
               } else
                  rpnStack.Push (item->sa->GetBuffer(),
                                 item->sa->getAttr()->GetLen());
            }
         } else { 
            PString p;
            p = "Corrupted expression near '";
            p += (char)(item->type) + "'";
            PError::InternalError (p.gets());
            return;
         }
         break;
      }

   if (rpnStack.Size() != 1) {
      PrintError (E_UNKNOWN_ERROR, "Corrupted expression stack");
   } else {
      b1 = rpnStack.Pop();
      if (b1.isNumber())
         value->FromDouble (b1.getNum());
      else
         value->FromString (b1.getStr()); 
   }
}

/*
   O^O Inserita l'analisi delle condizioni (tabcond)
*/

StmtAttr *
Expression::getValueUpdated ()
{
   if (stack.GetItemNum() > 1)   
      Eval();
   return getValue ();
}


unsigned int
Expression::getHighestStmtRelOrder()
{
   unsigned int Return = 0;
   StmtAttr *sa;
   for (sa = DbField.GetFirst(); sa; sa = DbField.GetNext())
      if (sa->GetStmtParent()->getOrder() > Return)
         Return = sa->GetStmtParent()->getOrder();
   return Return;
}
