package GenRGenS.master;

import java_cup.runtime.*;
import java.util.*;
import GenRGenS.utility.*;
import GenRGenS.master.distributions.*;
import GenRGenS.*;
import java.io.*;
import java.lang.reflect.*;

/*********************************************************
 *  Analyse des fichiers de description markoviens       *
 *********************************************************/

/* Declaration de variables et methodes utilitaires ... */

parser code
{:

  private boolean _parseError = false;

  public boolean isParseError()
  { return _parseError; }

  public Hashtable getSymbol2Generator()
  {
    return action_obj.getSymbol2Generator();
  }

  public Hashtable getSymbol2DistributionLaw()
  {
    return action_obj.getSymbol2DistributionLaw();
  }

  public Generator getMainGenerator()
  {
    return action_obj.getMainGenerator();
  }

  public boolean isWords()
  {
    return action_obj.isWords();
  }

  public void report_error(String message, Object info)
  {
    _parseError = true;
    if (info instanceof Symbol)
    {
      Symbol cur_token = (Symbol) info;
      int[] coord_left = ((GenRGenSScanner) getScanner()).getRelativePos(cur_token.left);
      int[] coord_right = ((GenRGenSScanner) getScanner()).getRelativePos(cur_token.right);
      if (coord_left[0] == coord_right[0])
      {
        GenRGenS.err.println("Syntax Error at line "+coord_right[0]+" chars "+ coord_left[1] +"-" + coord_right[1]);
      }
      else
      {
        GenRGenS.err.println("Syntax Error from line "+coord_left[0]+" char "+ coord_left[1] + " to line "+coord_right[0]+" char " + coord_right[1]);
      }
    }
    else
    {
      if (info instanceof String)
      {

        int[] coord_left = ((GenRGenSScanner) getScanner()).getRelativePos(cur_token.left);
        int[] coord_right = ((GenRGenSScanner) getScanner()).getRelativePos(cur_token.right);
        if (coord_left[0] == coord_right[0])
        {
          GenRGenS.err.println("At line "+coord_right[0]+" chars "+ coord_left[1] +"-" + coord_right[1] +" : " + message);
        }
        else
        {
          GenRGenS.err.println("From line "+coord_left[0]+" char "+ coord_left[1] + " to line "+coord_right[0]+" char " + coord_right[1] +" : " + message);
        }
      }
      else
      {
         GenRGenS.err.println(message);
      }
    }
  }
  public void report_fatal_error(String message, Object info) throws java.lang.Exception
  {
    _parseError = true;
    report_error(message,info);
    if (info instanceof Symbol)
    {
      Symbol cur_token = (Symbol) info;
      int[] coord_left = ((GenRGenSScanner) getScanner()).getRelativePos(cur_token.left);
      int[] coord_right = ((GenRGenSScanner) getScanner()).getRelativePos(cur_token.right);
      if (coord_left[0] == coord_right[0])
      {
        GenRGenS.err.println("Couldn't repair and continue parse at line "+coord_right[0]+".");
      }
      else
      {
        GenRGenS.err.println("Couldn't repair and continue parse at line "+coord_right[0]+".");
      }
    }
    else
    {
      Symbol current_token = cur_token;
      int[] coord_right = ((GenRGenSScanner) getScanner()).getRelativePos(current_token.right);
        GenRGenS.err.println("Couldn't repair and continue parse at line "+ coord_right[0]+".");
    }
    throw new Exception("Can't recover from previous error(s)");
  }

:}

action code
{:
  
  private Generator MainGenerator = null;
  private Hashtable Symbol2Generator = new Hashtable(7);
  private Hashtable Symbol2DistributionLaw = new Hashtable(7);
  private boolean words = false;
  
  public boolean isWords()
  {
    return words;
  }
  
  public Generator getMainGenerator()
  {
    return MainGenerator;
  }
  
  public void setMainFile(String path)
  {
    try
    {
      path = Master.masterFileDir+File.separator+path;
      if (GenRGenS.verbose)
      {
        GenRGenS.err.println("Parsing file : \""+path+"\" ...");
      }
      FileReader fr = new FileReader(path);
      String type = Linker.getDescriptionFileHeader(fr);
      File f = new File(path);
      Class GeneratorClass = Linker.getGeneratorClassFromType(type);
      if (GeneratorClass != null)
      {
        Generator gen = null;
        if (GenRGenS.verbose)
        {
          GenRGenS.err.println("  Searching a generator for type : \""+type+"\" ...");
        }
        try
        {
          Object[] args = new Object[1];
          Constructor[] tab = GeneratorClass.getConstructors() ;
          boolean found = false;
          for (int i = 0; (i<tab.length)/*&&(!found)*/;i++)
          { 
            Constructor c = tab[i];
            Class[] argsTypes = c.getParameterTypes();
          
            if (argsTypes.length == 1)
            {
              Class param = argsTypes[0];
              if (param.isAssignableFrom(f.getClass()))
              {
                found = true;
                args[0] = f;
                gen = (Generator) c.newInstance(args);
              }
            }
          }
          if (!found) 
          {
            parser.done_parsing();
            parser.report_error("While parsing\""+path+"\" :\n  No constructor for type \""+type+"\".",null);
          }
        }
        catch(InvocationTargetException in)
        {
          parser.done_parsing();
          parser.report_error("While parsing\""+path+"\" :\n  "+in.getTargetException(),null);
        }
        if (gen != null)
        {
          MainGenerator = gen;
        }
      }
    }
    catch(FileNotFoundException e2)
    {
      parser.done_parsing();
      parser.report_error("File \""+path+"\" not found.",null);
    }
    catch(GeneratorCreationException e3)
    {
      parser.done_parsing();
      parser.report_error("While parsing\""+path+"\" :\n  "+e3.toString(),null);
    }
    catch(IOException e4)
    {
      parser.done_parsing();
      parser.report_error("While parsing\""+path+"\" :\n  I\\O Error !",null);
    }
    catch(IllegalAccessException e5)
    {
      parser.done_parsing();
      parser.report_error("Illegal Class Access.",null);
    }
    catch(InstantiationException e6)
    {
      parser.done_parsing();
      parser.report_error(e6.toString(),null);
    }
  }
  
  public void link(String nt,String path,DistributionLaw d)
  {
    try
    {
      if (MainGenerator != null)
      {
        if (!MainGenerator.isTerminalSymbol(nt))
        {
          parser.done_parsing();
          parser.report_error("No symbol named \""+nt+"\" in the main file !",null);
        }
      }
      
      path = Master.masterFileDir+File.separator+path;
      if (GenRGenS.verbose)
      {
        GenRGenS.err.println("Parsing file : \""+path+"\" ...");
      }
      FileReader fr = new FileReader(path);
      String type = Linker.getDescriptionFileHeader(fr);
      File f = new File(path);
      Class GeneratorClass = Linker.getGeneratorClassFromType(type);
      if (GeneratorClass != null)
      {
        Generator gen = null;
        if (GenRGenS.verbose)
        {
          GenRGenS.err.println("  Searching a generator for type : \""+type+"\" ...");
        }
        try
        {
          Object[] args = new Object[1];
          Constructor[] tab = GeneratorClass.getConstructors() ;
          boolean found = false;
          for (int i = 0; (i<tab.length)/*&&(!found)*/;i++)
          { 
            Constructor c = tab[i];
            Class[] argsTypes = c.getParameterTypes();
          
            if (argsTypes.length == 1)
            {
              Class param = argsTypes[0];
              if (param.isAssignableFrom(f.getClass()))
              {
                found = true;
                args[0] = f;
                gen = (Generator) c.newInstance(args);
              }
            }
          }
          if (!found) 
          {
            parser.done_parsing();
            parser.report_error("While parsing\""+path+"\" :\n  No constructor for type \""+type+"\".",null);
          }
        }
        catch(InvocationTargetException in)
        {
          parser.done_parsing();
          parser.report_error("While parsing\""+path+"\" :\n  "+in.getTargetException(),null);
        }
        if (gen != null)
        {
          if (GenRGenS.verbose)
          {
            GenRGenS.err.println("Generator built for symbol \""+nt+"\" from file \""+path+"\"");
          }
          if (Symbol2Generator.containsKey(nt))
          {
            parser.done_parsing();
            parser.report_error("Attempt to assign 2 different generators to symbol \""+nt+"\" !",null);
          }
          else
          {
            Symbol2Generator.put(nt,gen);
            try
            {
              Symbol2DistributionLaw.put(nt,d);
            }
            catch(Exception e)
            { 
              parser.done_parsing();
              parser.report_error(e.toString(),null);
            }
          }
        }
      }
    }
    catch(FileNotFoundException e2)
    {
      parser.done_parsing();
      parser.report_error("File \""+path+"\" not found.",null);
    }
    catch(GeneratorCreationException e3)
    {
      parser.done_parsing();
      parser.report_error("While parsing\""+path+"\" :\n  "+e3.toString(),null);
    }
    catch(IOException e4)
    {
      parser.done_parsing();
      parser.report_error("While parsing\""+path+"\" :\n  I\\O Error !",null);
    }
    catch(IllegalAccessException e5)
    {
      parser.done_parsing();
      parser.report_error("Illegal Class Access.",null);
    }
    catch(InstantiationException e6)
    {
      parser.done_parsing();
      parser.report_error(e6.toString(),null);
    }
    
  }

  public Hashtable getSymbol2Generator()
  {
    return Symbol2Generator;
  }

  public Hashtable getSymbol2DistributionLaw()
  {
    return Symbol2DistributionLaw;
  }

:}


/* Declaration des  terminaux */

/* Tokens associés ?des mots-clés */

  /* Mots-clés généraux */
    terminal           TYPE;
    terminal           SYMBOLS, WORDS, LETTERS;
    terminal           EQUALS, SEMI, DBLPOINT, ARROW, ALIASES;
    /* ARROW est le mot cl?de sémantique : "est dériv?en ..." */

  /* Mots-clés spécifiques aux fichiers de description de Markov */
    terminal           START, FREQUENCIES;
    terminal           ORDER, PHASE;

  /* Mots-clés spécifiques aux fichiers "maitres" */
    terminal           MASTER, MAINFILE, WHERE, GAUSSIAN, NORMAL, RANDOM, UNIFORM;
    terminal           LOG, POW, PLUS, MINUS, TIMES, DIVIDED, CIRC, LPAREN, RPAREN;
    terminal           FLOOR, CEIL, LENGTH, SIZE, VIRG, ROUND, MIN, MAX, VAR;
    

/* Token "porteurs d'information" */
  terminal  Long     ENTIER;
  terminal  Double   REEL;
  terminal  String   IDENT;
  terminal  String   STRING;



/* Non-terminals */
non terminal         MasterDescription;
non terminal         TypeDeclaration, SymbolsDeclaration, MainFileDeclaration;
non terminal         WhereDeclaration, SubstitutionsDeclaration, Substitution;
non terminal DistributionLaw  Law;

non terminal Stack   StringList, IntList;


precedence left PLUS, MINUS;
precedence left TIMES, DIVIDED;
precedence left CIRC;


/* The grammar */

start with MasterDescription;


MasterDescription ::=
    TypeDeclaration SymbolsDeclaration MainFileDeclaration WhereDeclaration {: :};


TypeDeclaration ::=
    TYPE EQUALS MASTER                        {: :}
  ;

SymbolsDeclaration ::=
    SYMBOLS EQUALS WORDS                      {: words = true; :}
  | SYMBOLS EQUALS LETTERS                    {: words = false;:}
  |                                           {: words = true; :}
  ;

MainFileDeclaration ::=
    MAINFILE EQUALS STRING:s                    {: setMainFile(s); :}
  ;

WhereDeclaration ::=
    WHERE SubstitutionsDeclaration              {:
                                                :}
  |                                             {:
                                                :}
  ;

SubstitutionsDeclaration ::=
    Substitution                                {:
                                                :}
  ;

Substitution ::=
    Substitution IDENT:nt DBLPOINT STRING:path SIZE EQUALS Law:e1              
    																						{: 
                                                  link(nt,path,e1);
                                                :}
  | Substitution STRING:nt DBLPOINT STRING:path  SIZE EQUALS Law:e1
    																						{: 
                                                  link(nt,path,e1);
                                                :}
  |                                             {: 
                                                  
                                                :}
  ;  

Law ::=
	Law:e1 PLUS Law:e2	  {:
												  RESULT = new Addition(e1,e2);
												:}
  | Law:e1 MINUS Law:e2	{:
												  RESULT = new Substraction(e1,e2);
												:}
  | Law:e1 TIMES Law:e2	{:
												  RESULT = new Product(e1,e2);
												:}
  | Law:e1 DIVIDED Law:e2
            						{:
												  RESULT = new Division(e1,e2);
												:}
  | Law:e1 CIRC Law:e2
  									    {:
												  RESULT = new Pow(e1,e2);
												:}
  | LPAREN Law:e1 RPAREN	    
  											{:
												  RESULT = e1;
												:}
  | POW LPAREN Law:e1 VIRG Law:e2 RPAREN	    
  											{:
												  RESULT = new Pow(e1,e2);
												:}
  | LOG LPAREN Law:e1 VIRG Law:e2 RPAREN	    
  											{:
												  RESULT = new Log(e1,e2);
												:}
  | LOG LPAREN Law:e1 RPAREN	    
  											{:
												  RESULT = new Log(new ConstantLong(2),e1);
												:}
  | FLOOR LPAREN Law:e RPAREN					
  											{:
												  RESULT = new Floor(e);
												:}
  | ROUND LPAREN Law:e RPAREN					
  											{:
												  RESULT = new Round(e);
												:}
  | CEIL LPAREN Law:e RPAREN					
  											{:
												  RESULT = new Ceil(e);
												:}
  | MIN LPAREN Law:e1 VIRG Law:e2 RPAREN					
  											{:
												  RESULT = new Min(e1,e2);
												:}
  | MAX LPAREN Law:e1 VIRG Law:e2 RPAREN					
  											{:
												  RESULT = new Max(e1,e2);
												:}
  | LENGTH							{:
												  RESULT = new SequenceLength();
												:}
  | SIZE  							{:
												  RESULT = new SequenceLength();
												:}
  | NORMAL LPAREN Law:mean VIRG Law:standardDev RPAREN							
  											{:
												  RESULT = new Gaussian(mean,standardDev);
												:}
  | GAUSSIAN LPAREN Law:mean VIRG Law:standardDev RPAREN							
  											{:
												  RESULT = new Gaussian(mean,standardDev);
												:}
  | RANDOM LPAREN Law:from VIRG Law:to RPAREN							
  											{:
												  RESULT = new Uniform(from,to);
												:}
  | RANDOM LPAREN Law:to RPAREN
  											{:
												  RESULT = new Uniform(new ConstantDouble(0.0),to);
												:}
  | UNIFORM LPAREN Law:from VIRG Law:to RPAREN							
  											{:
												  RESULT = new Uniform(from,to);
												:}
  | UNIFORM LPAREN Law:to RPAREN
  											{:
												  RESULT = new Uniform(new ConstantDouble(0.0),to);
												:}
  | ENTIER : n							
  											{:
												  RESULT = new ConstantLong(n.longValue());
												:}
  | REEL : f							
  											{:
												  RESULT = new ConstantDouble(f.doubleValue());
												:}
  | VAR LPAREN IDENT:s VIRG Law:l RPAREN
  											{:
  											  VarDecl target = VarDecl.getVar(s);
  											  if(target != null)
												  {
                            parser.done_parsing();
                            parser.report_error("Attempt to declare twice a variable named \""+s+"\" detected.",null);
												  }
												  else
												  {
												    RESULT = new VarDecl(s,l);
												  }
												:}
  | IDENT : i							
  											{:
												  if(i.toLowerCase().equals("n"))
												  {
												    RESULT = new SequenceLength();
												  }
												  else
												  {
  											    VarDecl t = VarDecl.getVar(i);
  											    if(t == null)
												    {
                              parser.done_parsing();
                              parser.report_error("Unknown variable/function \""+i+"\" !\nThis variable's first use should precede its first use.",null);
												    }
												    else
												    {
												      RESULT = new Var(t);
												    }
												  }
												:}
  ;
