package GenRGenS.grammar;

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

%%

%yylexthrow{ 
LexicalException
%yylexthrow} 

%class GenRGenSScanner
%cup
%{
  private Hashtable _keywords = new Hashtable(51);
  private Hashtable _identifiers = new Hashtable(101);

  private Vector _lines = new Vector();

  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;
  }
  
  String _buff = "";
  public void resetBuffer()
  {
    _buff = "";
  }
  public void appendBuffer(String s)
  {
    _buff+=s;
  }
  public String getBuffer()
  {
    return(_buff);
  }

%}

%init{
    /* Here is the place to add new keywords (uppercase keywords only)... */

    _keywords.put("TYPE",new Integer(sym.TYPE));
    _keywords.put("GRAMMAR",new Integer(sym.GRAMMAR));
    _keywords.put("MARKOV",new Integer(sym.MARKOV));
    _keywords.put("MASTERFILE",new Integer(sym.MASTERFILE));
    _keywords.put("SYMBOLS",new Integer(sym.SYMBOLS));
    _keywords.put("WORDS",new Integer(sym.WORDS));
    _keywords.put("LETTERS",new Integer(sym.LETTERS));
    _keywords.put("EQUALS",new Integer(sym.EQUALS));
    _keywords.put("START",new Integer(sym.START));
    _keywords.put("FREQUENCIES",new Integer(sym.FREQUENCIES));
    _keywords.put("ORDER",new Integer(sym.ORDER));
    _keywords.put("PHASE",new Integer(sym.PHASE));
    _keywords.put("EXACT",new Integer(sym.EXACT));
    _keywords.put("RULES",new Integer(sym.RULES));
    _keywords.put("WEIGHTS",new Integer(sym.WEIGHTS));
    _keywords.put("POISSON",new Integer(sym.POISSON));
    _keywords.put("ALIASES",new Integer(sym.ALIASES));
%init}

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


ALPHA = [A-Za-z]

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

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

%%
<YYINITIAL>
{
  "/*"          { 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)); }
  \"            { resetBuffer(); yybegin(STRING); }
  {Ident}       {
                  // First we check if the identifier is a keyword ...
                  String ident = yytext();
                  //System.err.println(ident.toUpperCase());
                  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 Integer(Integer.parseInt(yytext())))); }
  {FLOAT}       { return (new Symbol(sym.REEL,yychar,yychar+yytext().length(),new Float(Float.parseFloat(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>
{
  "*/"          { yybegin(YYINITIAL); }
  {NEWLINE}     { addLine(yychar+yytext().length()); }
   .            {  }
}

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


<STRING>
{
  {NEWLINE}     { 
                  GenRGenS.err.println("Error while lexing line "+yyline+" cars "+ yycolumn + "-"+(yycolumn+yytext().length())+" :\n   Passed the end of line during String definition."); 
                  throw (new LexicalException());
                }
  \\t           { appendBuffer("\t"); }
  \\n           { appendBuffer("\n"); }
  \\r           { appendBuffer("\r"); }
  \\\           { appendBuffer("\\"); }
  \"            { yybegin(YYINITIAL); return (new Symbol(sym.STRING,yychar-getBuffer().length(),yychar,getBuffer())); }
  .             { appendBuffer(yytext());}
}
