package GenRGenS.master;

import java_cup.runtime.*;
import java.util.*;
import GenRGenS.master.sym;
import GenRGenS.*;

  /**
   * Les instances de la classe GenRGenSScanner sont des Lexer renvoyant à chaque appel
   * à nextToken() une erreur ou un token dans le format attendu par java_cup.
   */

%%

%yylexthrow{ 
LexicalException
%yylexthrow} 


%class GenRGenSScanner
%cup
%{
  private Hashtable _keywords = new Hashtable(51);
  private Hashtable _identifiers = new Hashtable(101);
  
  private Vector _lines = new Vector();
  
  private int _openedPar = 0;
  
  
  private String _stringContent = "";
  private boolean _insideString = false;
  private boolean _backslashOpened = false;
  
  public boolean unclosedComment()
  {
    return (_openedPar > 0);
  }
  
  public boolean unfinishedString()
  {
    return _insideString;
  }

  private void addLine(int car_number)
  {
    _lines.add(new Integer(car_number));
  }
  
  /**
   * Transforme une position absolue de caractère dans le fichier 
   * en cours de lexing en un tableau constitué de ses 2 coordonnées
   * relatives (ligne, colonne).
   * @returns Un tableau de 2 éléments où le premier 
   * élément est le numero de ligne du caractère de position
   * fournie en entrée et le 2ème élément est le numéro de colonne
   * de ce caractère.
   */
  public int[] getRelativePos(int AbsPos)
  {
    int i = 0;
    boolean found = false;
    int lastLineStart = 0;
    int[] result = new int[2];
    int lineStart = 0;
    while( (i<_lines.size()) && !found)
    {
      lastLineStart = lineStart;
      lineStart = ((Integer)_lines.elementAt(i)).intValue();
      
      if (AbsPos < lineStart)
      {
        found = true;
      }
      else
      {
        i++;
      }
    }
    if(found)
    {
      result[0] = i+1;
      result[1] = AbsPos - lastLineStart+1;
    }
    else
    {
      result[0] = (i==0) ? 1 : i+1;
      result[1] = AbsPos - lineStart+1;
    }
    return result;
  }
  
%}

%init{
    /* Here is the place to add new keywords (uppercase keywords only)... */
    
    _keywords.put("TYPE",new Integer(sym.TYPE));
    _keywords.put("MASTER",new Integer(sym.MASTER));
    _keywords.put("MAINFILE",new Integer(sym.MAINFILE));
    _keywords.put("WHERE",new Integer(sym.WHERE));
    _keywords.put("LETTERS",new Integer(sym.LETTERS));
    _keywords.put("SYMBOLS",new Integer(sym.SYMBOLS));
    _keywords.put("WORDS",new Integer(sym.WORDS));
    _keywords.put("EQUALS",new Integer(sym.EQUALS));
    _keywords.put("START",new Integer(sym.START));
    _keywords.put("ALIASES",new Integer(sym.ALIASES));
    _keywords.put("LENGTH",new Integer(sym.LENGTH));
    _keywords.put("LOG",new Integer(sym.LOG));
    _keywords.put("POW",new Integer(sym.POW));
    _keywords.put("FLOOR",new Integer(sym.FLOOR));
    _keywords.put("CEIL",new Integer(sym.CEIL));
    _keywords.put("NORMAL",new Integer(sym.NORMAL));
    _keywords.put("GAUSSIAN",new Integer(sym.GAUSSIAN));
    _keywords.put("RANDOM",new Integer(sym.RANDOM));
    _keywords.put("UNIFORM",new Integer(sym.UNIFORM));
    _keywords.put("LOG",new Integer(sym.LOG));
    _keywords.put("POW",new Integer(sym.POW));
    _keywords.put("FLOOR",new Integer(sym.FLOOR));
    _keywords.put("CEIL",new Integer(sym.CEIL));
    _keywords.put("LENGTH",new Integer(sym.LENGTH));
    _keywords.put("SIZE",new Integer(sym.SIZE));
    _keywords.put("ROUND",new Integer(sym.ROUND));
    _keywords.put("VAR",new Integer(sym.VAR));
    _keywords.put("MAX",new Integer(sym.MAX));
    _keywords.put("MIN",new Integer(sym.MIN));
%init}

%full
%line
%char
%column
%state EOL_COMMENT, COMMENT, STRING


ALPHA = [A-Za-z]

INT = [0-9]+
FLOAT = [0-9]+ [.] [0-9]*
Ident = ("_"|{ALPHA}) ({ALPHA}|{INT}|"_"|"."|"$"|"@")* ("'")*

SPACE = \t|" "
NEWLINE= \r|\n|\r\n
SEPARATOR = {SPACE}|{NEWLINE}

%%
<YYINITIAL>
{
  "/*"          { 
                  _openedPar++;
                  yybegin(COMMENT); 
                }
  "//"          { yybegin(EOL_COMMENT); }
  "::="         { return (new Symbol(sym.ARROW,yychar,yychar+yytext().length(),null)); }
  "->"          { return (new Symbol(sym.ARROW,yychar,yychar+yytext().length(),null)); }
  "="           { return (new Symbol(sym.EQUALS,yychar,yychar+yytext().length(),null)); }
  ";"           { return (new Symbol(sym.SEMI,yychar,yychar+yytext().length(),null)); }
  ":"           { return (new Symbol(sym.DBLPOINT,yychar,yychar+yytext().length(),null)); } 
  "+"           { return (new Symbol(sym.PLUS,yychar,yychar+yytext().length(),null)); } 
  "-"           { return (new Symbol(sym.MINUS,yychar,yychar+yytext().length(),null)); } 
  "*"           { return (new Symbol(sym.TIMES,yychar,yychar+yytext().length(),null)); } 
  "/"           { return (new Symbol(sym.DIVIDED,yychar,yychar+yytext().length(),null)); } 
  "^"           { return (new Symbol(sym.CIRC,yychar,yychar+yytext().length(),null)); } 
  "("           { return (new Symbol(sym.LPAREN,yychar,yychar+yytext().length(),null)); } 
  ")"           { return (new Symbol(sym.RPAREN,yychar,yychar+yytext().length(),null)); } 
  ","           { return (new Symbol(sym.VIRG,yychar,yychar+yytext().length(),null)); } 
  \"            { 
                  _insideString = true;
                  _stringContent = "";
                  yybegin(STRING); 
                }
  {Ident}       { 
                  // First we check if the identifier is a keyword ...
                  String ident = yytext();
                  Integer token = (Integer) _keywords.get(ident.toUpperCase());
                  if (token!=null)
                  {
                    return (new Symbol(token.intValue(),yychar,yychar+ident.length(),null));
                  }
                  else
                  {
                    /*
                      ... if our attempt to see the ident as a keyword fails, 
                      then it's a real identifier. We don't want to create as many
                      strings as there are occurences for an identifier (especially 
                      when using WORDS options...). Therefore, we use a HashTable to 
                      store encountered strings. that's why we have to check if a 
                      string belongs to the HashTable before creating a new Token...
                    */
                    String ident2 = (String) _identifiers.get(ident);
                    if (ident2 != null)
                    { ident = ident2; }
                    else
                    { _identifiers.put(ident,ident); }
                    return (new Symbol(sym.IDENT,yychar,yychar+ident.length(),ident)); 
                  }
                }
  {INT}         { return (new Symbol(sym.ENTIER,yychar,yychar+yytext().length(),new Long(Long.parseLong(yytext())))); }
  {FLOAT}       { return (new Symbol(sym.REEL,yychar,yychar+yytext().length(),new Double(Double.parseDouble(yytext().replace(',','.'))))); }
  {SPACE}       {  }
  {NEWLINE}     { addLine(yychar+yytext().length()); }
  .             { 
                  GenRGenS.err.println("Error while lexing line "+yyline+" cars "+ yycolumn + "-"+(yycolumn+yytext().length())+" :\n   "+'"' + yytext()+'"' + " is not a legal lexical unit !"); 
                  throw (new LexicalException());
                }
  
}

<COMMENT>
{
  "*/"          { 
                  _openedPar--;
                  yybegin(YYINITIAL); 
                }
  {NEWLINE}     { addLine(yychar+yytext().length()); }
   .            {  }
}

<EOL_COMMENT>
{
  {NEWLINE}     { 
                  addLine(yychar+yytext().length());
                  yybegin(YYINITIAL); 
                }
  .             {  }
}

<STRING> 
{
  \"            { 
                  yybegin(YYINITIAL); 
                  _insideString = false;
                  return (new Symbol(sym.STRING,yychar,yychar+yytext().length(),_stringContent));                   
                }
  [^\n\r\"\\]+  { _stringContent = _stringContent.concat( yytext() ); }
  \\t           { _stringContent = _stringContent.concat("\t"); }
  \\n           { _stringContent = _stringContent.concat("\n"); }
  \\r           { _stringContent = _stringContent.concat("\r"); }
  \\\           { _stringContent = _stringContent.concat("\\"); }
  {NEWLINE}     { 
                  GenRGenS.err.println("Error while lexing line "+yyline+" cars "+ yycolumn + "-"+(yycolumn+yytext().length())+" :\n   "+"A String can't go through a end of line !");
                  throw (new LexicalException());
                }
}
