/***************************************************************************
 *  aGrUM modified frames and atg files for cocoR
 *   Copyright (c) 2005-2024 by Pierre-Henri WUILLEMIN(@LIP6) and Christophe GONZALES(@AMU)
 *   info_at_agrum_dot_org
***************************************************************************/
/*----------------------------------------------------------------------
Compiler Generator Coco/R,
Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz
extended by M. Loeberbauer & A. Woess, Univ. of Linz
ported to C++ by Csaba Balazs, University of Szeged
with improvements by Pat Terry, Rhodes University

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.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

As an exception, it is allowed to write an extension of Coco/R that is
used as a plugin in non-free software.

If not otherwise stated, any source code generated by Coco/R (other than
Coco/R itself) does not fall under the GNU General Public License.
-----------------------------------------------------------------------*/


#include <memory.h>
#include <string.h>
#include "Scanner.h"

#if defined(__GNUC__) || defined(__clang__)
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored "-Wsign-compare"
#endif

   namespace gum {
namespace formula {


// Token, Buffer, InMemoryBuffer, StreamBuffer, MappedBuffer, UTF8Buffer
// implementations are now in <agrum/base/core/cocoR/common.cpp>

Scanner::Scanner(const unsigned char* buf, int len, std::string filename, bool trace) {
  buffer = Buffer::CreateFromMemory( buf, len );
  filenamne__=widen( filename.c_str() );
  trace__=trace;
  Init();
}

Scanner::Scanner( const char* fileName,bool trace ) {
  Load( widen( std::string( fileName ) ).c_str() );
  trace__=trace;
}

Scanner::Scanner( const wchar_t* fileName,bool trace ) {
  Load( fileName );
  trace__=trace;
}

void Scanner::Load( const wchar_t* fileName ) {
  char* chFileName = coco_string_create_char( fileName );

  buffer = Buffer::CreateOptimal( chFileName );
  if ( buffer == nullptr ) {
    std::string s( "No such file : " ); s+=chFileName;
    coco_string_delete( chFileName );
    GUM_ERROR( gum::IOError,s );
  }

  coco_string_delete( chFileName );
  filenamne__=std::wstring( fileName );
  Init();
}

Scanner::Scanner( FILE* s,bool trace ) {
  buffer = Buffer::CreateOptimal( s, true );
  filenamne__=L"FILE";
  Init();
  trace__=trace;
}

Scanner::~Scanner() {
  char* cur = ( char* ) firstHeap;

  while ( cur != nullptr ) {
    cur = *( char** )( cur + HEAP_BLOCK_SIZE );
    free( firstHeap );
    firstHeap = cur;
  }

  if ( tval ) delete [] tval;

  if ( buffer ) delete buffer;
}

void Scanner::Init() {
  percent=-1;
  EOL    = '\n';
  eofSym = 0;
  	maxT = 10;
	noSym = 10;
	int i;
	for (i = 48; i <= 57; ++i) start.set(i, 7);
	for (i = 65; i <= 90; ++i) start.set(i, 6);
	for (i = 95; i <= 95; ++i) start.set(i, 6);
	for (i = 97; i <= 122; ++i) start.set(i, 6);
	start.set(43, 8);
	start.set(45, 9);
	for (i = 42; i <= 42; ++i) start.set(i, 4);
	for (i = 47; i <= 47; ++i) start.set(i, 4);
	for (i = 60; i <= 60; ++i) start.set(i, 4);
	for (i = 62; i <= 62; ++i) start.set(i, 4);
	for (i = 94; i <= 94; ++i) start.set(i, 4);
	start.set(10, 5);
	start.set(40, 13);
	start.set(41, 14);
	start.set(44, 15);
		start.set(Buffer::EoF, -1);


  tvalLength = 128;
  tval = new wchar_t[tvalLength]; // text of current token

  // HEAP_BLOCK_SIZE byte heap + pointer to next heap block
  heap = malloc( HEAP_BLOCK_SIZE + sizeof( void* ) );
  firstHeap = heap;
  heapEnd = ( void** )( ( ( char* ) heap ) + HEAP_BLOCK_SIZE );
  *heapEnd = 0;
  heapTop = heap;

  if ( sizeof( Token ) > HEAP_BLOCK_SIZE ) {
    wprintf( L"--- Too small HEAP_BLOCK_SIZE\n" );
    exit( 1 );
  }

  pos = -1; line = 1; col = 0; charPos = -1;
  oldEols = 0;
  NextCh();

  if ( ch == 0xEF ) { // check optional byte order mark for UTF-8
    NextCh(); int ch1 = ch;
    NextCh(); int ch2 = ch;

    if ( ch1 != 0xBB || ch2 != 0xBF ) {
      wprintf( L"Illegal byte order mark at start of file" );
      exit( 1 );
    }

    // Wrap the buffer in UTF8Buffer (takes ownership of the inner buffer)
    buffer = new UTF8Buffer( buffer, true ); col = 0; charPos = -1;
    NextCh();
  }

  
  pt = tokens = CreateToken(); // first token is a dummy
}

void Scanner::NextCh() {
  if ( oldEols > 0 ) { ch = EOL; oldEols--; }
  else {
    pos = buffer->GetPos();
    ch = buffer->Read();
    int p=buffer->GetPercent();

    if ( ch==Buffer::EoF ) {
      GUM_EMIT1( onLoad,200 );
    } else {
      if ( percent<p ) {
        percent=p;
        GUM_EMIT1( onLoad,percent );
      }
    }

    col++; charPos++;

    // replace isolated '\r' by '\n' in order to make
    // eol handling uniform across Windows, Unix and Mac
    if ( ch == L'\r' && buffer->Peek() != L'\n' ) ch = EOL;

    if ( ch == EOL ) { /*if (trace__) std::cout<<line<<std::endl;*/ line++; col = 0; }
  }

  
}

void Scanner::AddCh() {
  if ( tlen >= tvalLength ) {
    tvalLength *= 2;
    wchar_t* newBuf = new wchar_t[tvalLength];
    memcpy( newBuf, tval, tlen*sizeof( wchar_t ) );
    delete [] tval;
    tval = newBuf;
  }

  if ( ch != Buffer::EoF ) {
#if defined(_MSC_VER)
#  pragma warning(push)
#  pragma warning(disable: 4018)
#elif defined(__GNUC__) || defined(__clang__)
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored "-Wsign-compare"
#endif
    		tval[tlen++] = ch;
#if defined(_MSC_VER)
#  pragma warning(pop)
#elif defined(__GNUC__) || defined(__clang__)
#  pragma GCC diagnostic pop
#endif
    NextCh();
  }
}



void Scanner::CreateHeapBlock() {
  void* newHeap;
  char* cur = ( char* ) firstHeap;

  while ( ( ( char* ) tokens < cur ) || ( ( char* ) tokens > ( cur + HEAP_BLOCK_SIZE ) ) ) {
    cur = *( ( char** )( cur + HEAP_BLOCK_SIZE ) );
    free( firstHeap );
    firstHeap = cur;
  }

  // HEAP_BLOCK_SIZE byte heap + pointer to next heap block
  newHeap = malloc( HEAP_BLOCK_SIZE + sizeof( void* ) );
  *heapEnd = newHeap;
  heapEnd = ( void** )( ( ( char* ) newHeap ) + HEAP_BLOCK_SIZE );
  *heapEnd = 0;
  heap = newHeap;
  heapTop = heap;
}

Token* Scanner::CreateToken() {
  Token* t;

  if ( ( ( char* ) heapTop + ( int ) sizeof( Token ) ) >= ( char* ) heapEnd ) {
    CreateHeapBlock();
  }

  t = ( Token* ) heapTop;
  heapTop = ( void* )( ( char* ) heapTop + sizeof( Token ) );
  t->val = nullptr;
  t->next = nullptr;
  return t;
}

void Scanner::AppendVal( Token* t ) {
  int reqMem = ( tlen + 1 ) * sizeof( wchar_t );

  if ( ( ( char* ) heapTop + reqMem ) >= ( char* ) heapEnd ) {
    if ( reqMem > HEAP_BLOCK_SIZE ) {
      wprintf( L"--- Too long token value\n" );
      exit( 1 );
    }

    CreateHeapBlock();
  }

  t->val = ( wchar_t* ) heapTop;
  heapTop = ( void* )( ( char* ) heapTop + reqMem );

  wcsncpy( t->val, tval, tlen );
  t->val[tlen] = L'\0';
}

Token* Scanner::NextToken() {
  while ( ch == ' ' ||
          			(ch >= 9 && ch <= 10) || ch == 13
        ) NextCh();

  
  int recKind = noSym;
  int recEnd = pos;
  t = CreateToken();
  t->pos = pos; t->col = col; t->line = line; t->charPos = charPos;
  int state = start.state( ch );
  tlen = 0; AddCh();

  switch ( state ) {
    case -1: { t->kind = eofSym; break; } // NextCh already done

    case 0: {
case_0:

      if ( recKind != noSym ) {
        tlen = recEnd - t->pos;
        SetScannerBehindT();
      }

      t->kind = recKind; break;
    } // NextCh already done

    		case 1:
			case_1:
			if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_3;}
			else if (ch == L'+' || ch == L'-') {AddCh(); goto case_2;}
			else {goto case_0;}
		case 2:
			case_2:
			if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_3;}
			else {goto case_0;}
		case 3:
			case_3:
			recEnd = pos; recKind = 3;
			if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_3;}
			else {t->kind = 3; break;}
		case 4:
			{t->kind = 4; break;}
		case 5:
			{t->kind = 5; break;}
		case 6:
			case_6:
			recEnd = pos; recKind = 6;
			if (ch == L'.' || (ch >= L'0' && ch <= L'9') || (ch >= L'A' && ch <= L'Z') || ch == L'_' || (ch >= L'a' && ch <= L'z')) {AddCh(); goto case_6;}
			else {t->kind = 6; break;}
		case 7:
			case_7:
			recEnd = pos; recKind = 1;
			if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_7;}
			else if (ch == L'.') {AddCh(); goto case_10;}
			else if (ch == L'E' || ch == L'e') {AddCh(); goto case_1;}
			else {t->kind = 1; break;}
		case 8:
			recEnd = pos; recKind = 4;
			if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_11;}
			else {t->kind = 4; break;}
		case 9:
			recEnd = pos; recKind = 4;
			if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_11;}
			else {t->kind = 4; break;}
		case 10:
			case_10:
			if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_12;}
			else {goto case_0;}
		case 11:
			case_11:
			if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_11;}
			else if (ch == L'.') {AddCh(); goto case_10;}
			else if (ch == L'E' || ch == L'e') {AddCh(); goto case_1;}
			else {goto case_0;}
		case 12:
			case_12:
			recEnd = pos; recKind = 2;
			if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_12;}
			else if (ch == L'E' || ch == L'e') {AddCh(); goto case_1;}
			else {t->kind = 2; break;}
		case 13:
			{t->kind = 7; break;}
		case 14:
			{t->kind = 8; break;}
		case 15:
			{t->kind = 9; break;}

  }

  AppendVal( t );
  return t;
}

void Scanner::SetScannerBehindT() {
  buffer->SetPos( t->pos );
  NextCh();
  line = t->line; col = t->col; charPos = t->charPos;

  for ( int i = 0; i < tlen; i++ ) NextCh();
}

// get the next token (possibly a token already seen during peeking)
Token* Scanner::Scan() {
  if ( tokens->next == nullptr ) {
    return pt = tokens = NextToken();
  } else {
    pt = tokens = tokens->next;
    return tokens;
  }
}

// peek for the next token, ignore pragmas
Token* Scanner::Peek() {
  do {
    if ( pt->next == nullptr ) {
      pt->next = NextToken();
    }

    pt = pt->next;
  } while ( pt->kind > maxT ); // skip pragmas

  return pt;
}

// make sure that peeking starts at the current scan position
void Scanner::ResetPeek() {
  pt = tokens;
}

#if defined(__GNUC__) || defined(__clang__)
#  pragma GCC diagnostic pop
#endif

} // namespace
} // namespace

