/*
** Splint - annotation-assisted static program checker
** Copyright (C) 1994-2003 University of Virginia,
**         Massachusetts Institute of Technology
**
** 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 of the License, 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.
** 
** The GNU General Public License is available from http://www.gnu.org/ or
** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
** MA 02111-1307, USA.
**
** For information on splint: info@splint.org
** To report a bug: splint-bug@splint.org
** For more information: http://www.splint.org
*/
/*
** mtscanner.c
**
** Because flex is so lame, we can't use two flex scanners at once.  Instead,
** we have to write a manual scanner.  Should look into some real parser 
** generator tools one day...
*/

# include "splintMacros.nf"
# include "basic.h"
# include "mtgrammar.h"
# include "mtscanner.h"

static inputStream scanFile;  /* file to scan */
static mttok mtscanner_getNextToken (void);
static /*@only@*/ cstringTable tokenTable = cstringTable_undefined;
static bool isInitialized = FALSE;

/*@constant int MT_TOKENTABLESIZE@*/
# define MT_TOKENTABLESIZE 64

static void mtscanner_initMod (void)
{
  llassert (cstringTable_isUndefined (tokenTable));
  tokenTable = cstringTable_create (MT_TOKENTABLESIZE);

  cstringTable_insert (tokenTable, cstring_makeLiteral ("attribute"), MT_STATE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("global"), MT_GLOBAL);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("context"), MT_CONTEXT);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("oneof"), MT_ONEOF);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("defaults"), MT_DEFAULTS);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("default"), MT_DEFAULT);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("parameter"), MT_PARAMETER);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("result"), MT_RESULT);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("literal"), MT_LITERAL);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("null"), MT_NULL);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("reference"), MT_REFERENCE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("clause"), MT_CLAUSE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("annotations"), MT_ANNOTATIONS);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("merge"), MT_MERGE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("transfers"), MT_TRANSFERS);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("preconditions"), MT_PRECONDITIONS);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("postconditions"), MT_POSTCONDITIONS);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("losereference"), MT_LOSEREFERENCE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("error"), MT_ERROR);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("end"), MT_END);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("as"), MT_AS);

  /*
  ** C Types
  */

  cstringTable_insert (tokenTable, cstring_makeLiteral ("char"), MT_CHAR);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("int"), MT_INT);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("float"), MT_FLOAT);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("double"), MT_DOUBLE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("void"), MT_VOID);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("anytype"), MT_ANYTYPE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("integraltype"), MT_INTEGRALTYPE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("unsignedintegraltype"), MT_UNSIGNEDINTEGRALTYPE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("signedintegraltype"), MT_SIGNEDINTEGRALTYPE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("const"), MT_CONST);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("volatile"), MT_VOLATILE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("restrict"), MT_RESTRICT);

  /*
  ** Punctuation
  */

  cstringTable_insert (tokenTable, cstring_makeLiteral ("==>"), MT_ARROW);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("+"), MT_PLUS);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("*"), MT_STAR);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("{"), MT_LBRACE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("}"), MT_RBRACE);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("("), MT_LPAREN);
  cstringTable_insert (tokenTable, cstring_makeLiteral (")"), MT_RPAREN);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("["), MT_LBRACKET);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("]"), MT_RBRACKET);
  cstringTable_insert (tokenTable, cstring_makeLiteral (","), MT_COMMA);
  cstringTable_insert (tokenTable, cstring_makeLiteral ("|"), MT_BAR);

  isInitialized = TRUE;
}

void mtscanner_reset (inputStream sourceFile)
{
  if (!isInitialized) 
    {
      mtscanner_initMod ();
    }

  scanFile = sourceFile;
}

int mtlex (MTSTYPE *mtlval)
{
  llassert (isInitialized);

  /* This is important!  Bison expects this */

  /*@ignore@*/
  mtlval->tok = mtscanner_getNextToken ();
  DPRINTF (("Return token: %s", mttok_unparse (mtlval->tok)));
  llassert (fileloc_isDefined (mttok_getLoc (mtlval->tok)));
  return (mttok_getTok (mtlval->tok));
  /*@end@*/
}

static void skipComments (void)
{
  int tchar;
  bool gotone = FALSE;
  tchar = inputStream_peekChar (scanFile);

  if (tchar == (int) '/' && inputStream_peekNChar (scanFile, 1) == (int) '*') 
    {
      check ((int) '/' == inputStream_nextChar (scanFile));
      check ((int) '*' == inputStream_nextChar (scanFile));
      
      while ((tchar = inputStream_nextChar (scanFile)) != EOF) 
	{
	  if (tchar == (int) '*' && inputStream_peekChar (scanFile) == (int) '/')
	    {
	      tchar = inputStream_nextChar (scanFile);
	      break;
	    }
	}

      if (tchar == EOF)
	{
	  llfatalerror 
	    (cstring_makeLiteral ("Reached end of metastate file inside comment."));
	  BADBRANCH;
	}
      else
	{
	  check ((int) '/' == tchar);
	  gotone = TRUE;
	}
    }

  if (isspace (tchar)) 
    {
      while (isspace (inputStream_peekChar (scanFile)))
	{
	  tchar = inputStream_nextChar (scanFile);
	}

      gotone = TRUE;
    }

  if (gotone)
    {
      /* If there was a comment or whitespace, need to skip again... */
      skipComments ();
    }
}

static mttok mtscanner_getNextToken (void)
{
  int tchar;
  int mtcode;
  cstring tok;
  mttok res;
  fileloc loc;

  skipComments ();
  loc = fileloc_copy (g_currentloc);
  tchar = inputStream_nextChar (scanFile);

  if (tchar == EOF) 
    {
      return mttok_create (EOF, cstring_undefined, loc);
    }

  tok = cstring_newEmpty ();

  DPRINTF (("tchar: %c", (char) tchar));

  if (tchar == (int) '\"') 
    {
      bool escaped = FALSE;

      /* String literal */
      while ((tchar = inputStream_peekChar (scanFile)) != EOF) {
	if (escaped) {
	  escaped = FALSE;
	} else if (tchar == (int) '\\') {
	  escaped = TRUE;
	} else if (tchar == (int) '\"') {
	  break;
	} else {
	  ;
	}

	tok = cstring_appendChar (tok, (char) tchar);
	check (tchar == inputStream_nextChar (scanFile));
      }

      if (tchar == EOF)
	{
	  llfatalerror 
	    (cstring_makeLiteral ("Reached end of metastate file inside string literal."));
	}
      else
	{
	  check ((int) '\"' == inputStream_nextChar (scanFile));
	  return mttok_create (MT_STRINGLIT, tok, loc);
	}
    }
  
  tok = cstring_appendChar (tok, (char) tchar);

  DPRINTF (("tok: %s", tok));

  if (isalpha (tchar)) 
    {
      while ((tchar = inputStream_peekChar (scanFile)) != EOF) {
	if (!isalnum (tchar) && (tchar != (int) '_') && (tchar != (int) '$')) {
	  break;
	}
	
	tok = cstring_appendChar (tok, (char) tchar);
	check (tchar == inputStream_nextChar (scanFile));
      }

      mtcode = cstringTable_lookup (tokenTable, tok);

      if (mtcode == NOT_FOUND) {
	DPRINTF (("Returning identifier: %s", tok));
	return mttok_create (MT_IDENT, tok, loc);
      }
    } 
  else 
    {
      /* Read until next space */
      DPRINTF (("Here we are: %s", tok));

      while ((tchar = inputStream_peekChar (scanFile)) != EOF) {
	if (isspace (tchar) || isalnum (tchar)) {
	  break;
	}
	
	tok = cstring_appendChar (tok, (char) tchar);
	DPRINTF (("Here we are: %s", tok));
	check (tchar == inputStream_nextChar (scanFile));
      }

      DPRINTF (("Here we are: [%s]", tok));
      mtcode = cstringTable_lookup (tokenTable, tok);
      
      if (mtcode == NOT_FOUND) {
	mtcode = MT_BADTOK;
      }
    }
  
  DPRINTF (("Read %s / %d", tok, mtcode));
  cstring_free (tok);

  res = mttok_create (mtcode, cstring_undefined, loc);
  DPRINTF (("Return token: %s", mttok_unparse (res)));
  return res;
}

ctype mtscanner_lookupType (mttok tok)
{
  cstring tname;
  uentry ue;

  llassert (mttok_isIdentifier (tok));
  tname = mttok_observeText (tok);

  DPRINTF (("Lookup type:  %s", tname));

  ue = usymtab_lookupSafe (tname);

  if (uentry_isValid (ue) && uentry_isDatatype (ue))
    {
      DPRINTF (("Found it: %s / %s", uentry_unparse (ue),
		ctype_unparse (uentry_getAbstractType (ue))));
    
      return uentry_getAbstractType (ue);
    }
  else
    {
      ctype ct;
      ue = uentry_makeDatatype (tname, ctype_unknown, MAYBE, qual_createUnknown(),
				mttok_stealLoc (tok));
      DPRINTF (("Making mts entry: %s", uentry_unparse (ue)));
      ct = usymtab_supForwardTypeEntry (ue);
      DPRINTF (("Type: %s", ctype_unparse (ct)));
      return ct;
    }
}
