/*
_____       _    _    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 "lex.h"
static char rcsid[] = "$Id: lex.cpp 5.0 1999/10/08 10:17:43 picoSoft Exp $";
# include "dberror.h"
# include <string.h>
# include <stdlib.h>
# include <time.h>
    
PCLASSID (Token)
PCLASSID (Lex)

# define IsFirstNameChar(c) ((unsigned char)c >= 'A' && (unsigned char)c != '{' \
                                                     && (unsigned char)c != '}')
# define IsNameChar(c) ((unsigned char)c >= 'A' && (unsigned char)c != '{'  \
                                                && (unsigned char)c != '}' \
                     || (unsigned char)c >= '0' && c <= '9')

# define IsDigitChar(c) (c >= '0' && c <= '9')

static char *
substr(char *start, char *end)
{
   static char buffer[256];
   char *pnt = buffer;
   char ap = (*start == '\'' || *start == '"') ? *start : 0;

   for ( ; start < end; start++)
      if (*start == ap && start[1] == ap)
     *pnt++ = *start++;
      else
     *pnt++ = *start;
   *pnt++ = *end;
   *pnt = 0;
   return buffer; 
}

typedef struct s_token
{
   char * word;
   toknum token;
} fix_token;

Lex::~Lex()
{
   Token *tk;
   for (tk = intList.GetFirst(); tk != 0; tk = intList.DeleteCurrent())
      delete tk;
}

Lex::Lex (char *comLine) :
tklist(intList)
{
   Token tk;
   char *start;
   char *line;
   int braceOpen = 0;

   isfirst = PTrue;

   for (line = comLine; *line; line++) {
      switch (*line) {
      case '>':
         if (line[1] == '=') {
            start = line;
            line++;
            tk.token = _GE_;
            tk.word = substr(start, line);
         } else {
            tk.token = (toknum)*line;
            tk.word = *line;
         }
         tklist.AddItem (new Token(tk));
         break;
      case '<':
         if (line[1] == '=') {
            start = line;
            line++;
            tk.token = _LE_;
            tk.word = substr(start, line);
         } else if (line[1] == '>') {
            start = line;
            line++;
            tk.token = _NE_;
            tk.word = substr(start, line);
         } else {
            tk.token = (toknum)*line;
            tk.word = *line;
         }
         tklist.AddItem (new Token(tk));
         break;
      case '!':
         if (line[1] == '=') {
            start = line;
            line++;
            tk.token = _NE_;
            tk.word = substr(start, line);
         } else {
            tk.token = (toknum)*line;
            tk.word = *line;
         }
         tklist.AddItem (new Token(tk));
         break;
      case ' ':
      case '\t':
      case '\n':
      case '\r':
         break;
      case '\"':
         start = line;
         for (line++; *line; line++)
            if (*line == '"')
               if (line[1] == '"')
                  line++;
               else
                  break;
         tk.token = STR_LITERAL;
         tk.word = substr(start, line);
         tklist.AddItem (new Token(tk));
         break;
      case '\'':
         start = line;
         for (line++; *line; line++)
            if (*line == '\'')
               if (line[1] == '\'')
                  line++;
               else
                  break;
         tk.token = STR_LITERAL;
         tk.word = substr(start, line);
         tklist.AddItem (new Token(tk));
         break;
      case '{':
         for (start = line + 1; *start == ' '; start++)
            ;
         for (line++; *line; line++)
            if (*line == '}')
               break;
         if ((start[0] == 't' || start[0] == 'T') ||
             (start[0] == 'd' || start[0] == 'D')) {
            tk.token = TIMESTAMP;
            tk.word = substr(start, line);
            tklist.AddItem (new Token(tk));
         } else if (((start[0] == 'o' || start[0] == 'O') &&
                     (start[1] == 'j' || start[1] == 'J')) ||
                    ((start[0] == 'f' || start[0] == 'F') &&
                     (start[1] == 'n' || start[1] == 'N'))) {
            line = &start[1]; // le seq.di escape sono ignorate
            braceOpen++;
         } else {
            line = start - 1;
            tk.token = (toknum)'{';
            tk.word = '{';
            tklist.AddItem (new Token(tk));
         }
         break;
      case '}':
         if (braceOpen)
            braceOpen--;
         else {
            tk.token = (toknum)*line;
            tk.word = *line;
            tklist.AddItem (new Token(tk));
         }
         break;
      default:
         if (IsFirstNameChar(*line)) {
            start = line;
            for (line++; IsNameChar(*line); line++)
               ;
            line--;
            tk.token = UOK_WORD;
            tk.word = substr(start, line);
         } else if (IsDigitChar(*line)){
            start = line;
            for (line++; IsDigitChar(*line); line++)
               ;
            if (IsNameChar(*line)) {
               for (line++; IsNameChar(*line); line++)
                  ;
               line--;
               tk.token = UOK_WORD;
               tk.word = substr(start, line);
            } else {
               PBool goOn = PTrue;
               PBool dot = PFalse;
               while (goOn)
                  switch (*line) {
                  case '.':
                     if (dot) {
                        goOn = PFalse;
                     } else {
                        dot = PTrue;
                        line++;
                     }
                     break;
                  case '0':
                  case '1':
                  case '2':
                  case '3':
                  case '4':
                  case '5':
                  case '6':
                  case '7':
                  case '8':
                  case '9':
                     line++;
                     break;
                  case 'E':
                  case 'e':
                     if (IsDigitChar(*(line + 1)) || *(line + 1) == '-')
                        line += 2;
                     else
                        goOn = PFalse;
                     break;
                  default:
                     goOn = PFalse;
                     break;
                  }
               line--;
               tk.token = NUMBER;
               tk.word = substr(start, line);
            }
         } else {
            tk.token = (toknum)*line;
            tk.word = *line;
         }
         tklist.AddItem (new Token(tk));
         break;
      }
   }
}

Token *
Lex::GetToken ()
{
   Token * Return;
   if (isfirst)
       Return = tklist.GetFirst();
    else
       Return = tklist.GetNext();
    isfirst = PFalse;
    return Return;
}

Token *
Lex::UngetToken ()
{
   Token * Return;
   if ((Return = tklist.GetPrevious ()) == 0)
      isfirst = PTrue;
   return Return;
}

void
Lex::DeleteToken ()
{
   Token *tk = tklist.GetCurrent();
   if (tk)
      delete tk;
   if (tklist.DeleteCurrent () != 0)
      if (tklist.GetPrevious () == 0)
         isfirst = PTrue;
}
