package GenRGenS.grammar;

import java_cup.runtime.*;
import java.util.*;
import java.math.*;
import GenRGenS.*;

/*********************************************************
 *         Fichiers de descriptions "MARKOV"             *
 *********************************************************/

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

parser code
{:

  private boolean _parseError = false;

  public boolean isParseError()
  { return _parseError; }

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

  public String[] getTerminaux()
  { return action_obj.getTerminaux(); }

  public String[] getNonTerminaux()
  { return action_obj.getNonTerminaux(); }

  public ContextFreeGrammarProduction[] getProductions()
  { return action_obj.getProductions(); }

  public int getSymbolStart()
  { return action_obj.getSymbolStart(); }

  public BigDecimal[] getTerminalsMasses()
  { return action_obj.getTerminalsMasses(); }



  public void report_error(String message, Object info)
  {
    _parseError = true;
    if (info instanceof Symbol)
    {
      Symbol cur_token = (Symbol) info;
      if (cur_token.left != -1)
      {
      
        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
      {
        GenRGenS.err.println("Syntax Error, possibly because of a malformed clause !");
      }
    }
    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;
      if (cur_token.left != -1)
      {
        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
      {
        GenRGenS.err.println("Couldn't repair and continue parse.");
      }
    }
    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)");
  }

  public void syntax_error(Symbol cur_token) 
  {
    report_error("Syntax error", cur_token);  
  }
  public void unrecovered_syntax_error(Symbol cur_token)  throws java.lang.Exception
  {
    report_fatal_error("Couldn't repair and continue parse", cur_token);  
  }


:}

action code
{:
  private boolean _isWord = true;

//	private int numeroNouvelAuxiliaire=1;

	// index du symbol start
	private int symbolStart=0;
	private String _startName = "";


	// representation textuelle des symboles non-terminaux
	private String[] nonTerminaux;

  // representation textuelle des symboles terminaux
	private String[] terminaux;

	// representation des productions de la grammaire
  //  - une ligne par regle
  //  - le premier entier du tableau est le symbole productif (LHS)
  //  - les suivants sont les symboles produits (RHS)
	private ContextFreeGrammarProduction[] productions;


  private Hashtable _lexique = new Hashtable(31);
  private int _nextTokenID = 0;

  /* Variables relatives au stockage des règles lues */

  private Vector _tmpRules = new Vector();
  private Hashtable _nonTerminaux = new Hashtable();
  private Hashtable _indetermines = new Hashtable();


	/* Variables relatives au stockage des poids lus */
	private Vector tmpMasses = new Vector();
	private BigDecimal[] terminalsMasses = new BigDecimal[0];



  public boolean isWord()
  { return _isWord; }

  public String[] getTerminaux()
  { return terminaux; }

  public String[] getNonTerminaux()
  { return nonTerminaux; }

  public ContextFreeGrammarProduction[] getProductions()
  { return productions; }

  public int getSymbolStart()
  { return symbolStart;}

  public BigDecimal[] getTerminalsMasses()
  { return terminalsMasses; }


	private void setStartSymbolName(String s)
  {
    _startName = s;
  }

  /*
    Ajoute une règle ?la liste temporaire des règles "déj?lues".
    Met ?jour au passage les contenus des tables de Hashage _NonTerminaux
    et _Indetermines contenant respectivement les chaînes représentant les
    Non Terminaux avérés et les symboles de nature indeterminée.
  */
  private void addRule(String left, Stack right)
  {
    // On ajoute la partie gauche ?la liste des non terminaux
    if (!_nonTerminaux.containsKey(left))
    {
      _nonTerminaux.put(left,left);
    }
    if (_indetermines.containsKey(left))
    {
      _indetermines.remove(left);
    }

    // On ajoute les symboles de la partie droite ?la liste des indetermines
    for(int i=0; i<right.size() ; i++)
    {
      /*
        Une Stack dérive d'un Vector, on l'utilise ici comme
        tel. Cependant, on remarquera qu'ici, l'ordre des éléments
        tels qu'ils ont ét?rangés par l'opération "push" n'est pas
        important ...
        (on vérifie juste s'il ont déj?ét?rencontrés )
      */
      String s = (String) right.elementAt(i);
      if (!_nonTerminaux.containsKey(s))
      {
        if (!_indetermines.containsKey(s))
        {
          _indetermines.put(s,s);
        }
      }
    }

    // On ajoute la règle ?la liste des règles temporaires
    // La partie  gauche de la regle est la première rencontrée sur la pile
    right.push(left);
    /* Les règles sont ajoutées en dernière position dans l'ordre de rencontre
       => La règle ?l'adresse 0 est la première.
    */
    _tmpRules.add(0,right);
  }


    /*
      "Traduit" le nom d'un symbole en son index
      (<0 si terminal et >= 0 si non Terminal)
    */
		Hashtable lettre2int = new Hashtable();



  /*
    Met en forme les règle lues de façon ?ce qu'elle puisse être utilisées directement
    par la classe ContextFreeGrammar.
  */

  private void gatherRules()
  {
    // A ce niveau, les symboles indéterminés sont tous des terminaux ...
		int indexT = -1;
		int indexNT = 0;
		Stack tmpStack;
		String tmpString;
		Vector TokenisedRule;
		productions = new ContextFreeGrammarProduction[_tmpRules.size()];

		for(int i=0; i<_tmpRules.size(); i++)
		{
		  tmpStack = (Stack) _tmpRules.elementAt(i);
  		TokenisedRule = new Vector(tmpStack.size());

		  // On sait que la première chaine rencontrée est un non terminal ...
		  tmpString =  (String) tmpStack.pop();
      if (_startName.equals(""))
      {
        _startName = tmpString;
      }
		  // On lui affecte un numero si besoin est puis on l'ajoute au vecteur temporaire
		  if (! lettre2int.containsKey(tmpString))
		  {
		    lettre2int.put(tmpString,new Integer(indexNT));
		    TokenisedRule.add(new Integer(indexNT));
		    indexNT++;
		  }
		  else
		  {
		    TokenisedRule.add(lettre2int.get(tmpString));
		  }

		  while(!tmpStack.empty())
		  {
  		  tmpString =  (String) tmpStack.pop();

  		  if (! lettre2int.containsKey(tmpString))
		    {
		      if (_nonTerminaux.containsKey(tmpString))
		      {
		        lettre2int.put(tmpString,new Integer(indexNT));
		        TokenisedRule.add(new Integer(indexNT));
		        indexNT++;
		      }
		      if (_indetermines.containsKey(tmpString))
		      {
		        lettre2int.put(tmpString,new Integer(indexT));
		        TokenisedRule.add(new Integer(indexT));
		        indexT--;
		      }
		    }
		    else
		    {
		      TokenisedRule.add(lettre2int.get(tmpString));
		    }
		  }
 		  try
		  {
		    productions[i] = new ContextFreeGrammarProduction(TokenisedRule);
		  }
		  catch(BadGrammarException e)
		  {
        parser.done_parsing();
        parser.report_error("Internal error n?, please report this bug to the developing team !","");
		  }
		}
    //GenRGenS.err.println("Start = "+_startName);
    symbolStart = ((Integer) lettre2int.get(_startName)).intValue();

		/*
		  On remplit les tables contenant les representations en String
		  des terminaux et non terminaux
		*/
		terminaux = new String[_indetermines.size()];
		nonTerminaux = new String[_nonTerminaux.size()];
		Enumeration enu = lettre2int.keys();
    while(enu.hasMoreElements())
		{
		  String s = (String) enu.nextElement();
		  Integer i = (Integer) lettre2int.get(s);
		  if (i.intValue()<0)
		  { terminaux[-1-i.intValue()] = s; }
		  else
		  { nonTerminaux[i.intValue()] = s; }
		}

    /*
      On initialise la table des poids pour les terminaux
    */
    terminalsMasses = new BigDecimal[terminaux.length];
    BigDecimal one = new BigDecimal(1);
    for(int i=0;i<terminalsMasses.length;i++)
    {
      terminalsMasses[i] = one;
    }
  }

  /*
    Fonction puissance classique (définie pour des exposants positifs).
  */
  private int pow(int a, int b)
	{
		if (b==0)
		{
		  return 1;
		}
		else
		{
		  if (b%2==0)
		  {
				return(pow(a*a,b/2));
			}
			else
			{
				return(a*pow(a*a,b/2));
			}
		}
	}

  /*
    Procedure d'ajout d'un poids.
  */

  private void addWeight(String s, BigDecimal b)
  {
    if (b.compareTo(new BigDecimal(0)) <= 0)
    {
      parser.done_parsing();
      parser.report_error("Attempting to assign a negative "+b+" weight to symbol "+ s +" !",null);
    }
    if (lettre2int.containsKey(s))
    {
      Integer i = (Integer) lettre2int.get(s);
      if (i.intValue()<0)
      {
        terminalsMasses[-i.intValue()-1] = b;
      }
      else
      {
        parser.done_parsing();
        parser.report_error("Couldn't assign a weight to symbol \""+s+"\" : symbol is non-terminal !","");
      }

    }
    else
    {
      parser.done_parsing();
      parser.report_error("Couldn't assign a weight to symbol \""+s+"\" : symbol is unknown !","");
    }
  }


	/*
	  Modifie la representation en String (ah ah ah ...) d'un token :
	  un token de representation src prendra comme representation dest.
	*/
	private void replace(String src, String dest)
	{
    if (lettre2int.containsKey(src))
    {
      Integer i = (Integer) lettre2int.get(src);
      if (i.intValue()<0)
      {
        terminaux[-i.intValue()-1] = dest;
      }
      else
      {
        parser.done_parsing();
        parser.report_error("Attempting to alias an non terminal \""+ src +"\" symbol !","");
      }

    }
    else
    {
      parser.done_parsing();
      parser.report_error("Attempting to alias an undefined \""+ src +"\" symbol !","");
    }
	}

:}


/* Declaration des  terminaux */

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

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

  /* Mot clés spécifiques de la génération "grammaire"" */
    terminal           RULES, WEIGHTS;

  /* 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           QUOTES, EXACT, POISSON;

/* Token "porteurs d'information" */
  terminal  Integer  ENTIER;
  terminal  Float    REEL;
  terminal  String   IDENT;
  terminal  String   STRING;



/* Non-terminals */
non terminal         GrammarDescription;
non terminal         TypeDeclaration;
non terminal         SymbolTypeDeclaration, StartNTDeclaration;
non terminal         RulesDeclaration, RulesList;
non terminal         WeightsDeclaration, WeightsList;
non terminal         AliasesDeclaration, AliasesList;

non terminal BigDecimal   Real;

non terminal Stack   StringList, IntList;





precedence left ARROW;

precedence left IDENT;



/* The grammar */

start with GrammarDescription;


GrammarDescription ::=
    TypeDeclaration SymbolTypeDeclaration StartNTDeclaration
    RulesDeclaration WeightsDeclaration AliasesDeclaration 
  ;


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

SymbolTypeDeclaration ::=
    SYMBOLS EQUALS WORDS                        {: _isWord = true; :}
  | SYMBOLS EQUALS LETTERS                      {: _isWord = false; :}
  |                                             {: _isWord = true; :}
  ;

StartNTDeclaration ::=
    START EQUALS IDENT:s                        {: setStartSymbolName(s); :}
  |                                             {:  :}
  ;

RulesDeclaration ::=
    RULES EQUALS RulesList                      {: 
                                                   gatherRules(); 

                                                :}
  | RULES EQUALS                                {:
                                                  parser.done_parsing();
                                                  parser.report_error("You must define at least ONE rule !","");
                                                :}
  |                                             {:
                                                  parser.done_parsing();
                                                  parser.report_error("You must define at least ONE rule !","");
                                                :}
  ;


RulesList ::=
    IDENT:s ARROW StringList:l SEMI RulesList   {:
                                                  addRule(s,l);
                                                :}
  | IDENT:s ARROW StringList:l SEMI             {:
                                                  addRule(s,l);
                                                :}
  ;



StringList ::=
    IDENT:s StringList:p                        {:
                                                  p.push(s);
                                                  RESULT = p;
                                                :}
  |                                             {:
                                                  Stack p = new Stack();
                                                  RESULT = p;
                                                :}
  ;


Real ::=
    REEL:f                                      {:

                                                   RESULT = new BigDecimal(f.floatValue());
                                                :}
  | ENTIER:n                                    {:
                                                   RESULT = new BigDecimal(n.intValue());
                                                :}
  ;

WeightsDeclaration ::=
    WEIGHTS EQUALS WeightsList                  {:  :}
  |                                             {:  :}
  ;

WeightsList ::=
    IDENT:s Real:b WeightsList                  {: addWeight(s,b); :}
  |                                             {:
                                                :}
  ;

AliasesDeclaration ::=
    ALIASES EQUALS AliasesList                  {:  :}
  |                                             {:  :}
  ;

AliasesList ::=
    IDENT:i_src EQUALS IDENT:i_dest AliasesList {:
                                                  replace(i_src,i_dest);
                                                :}
  | IDENT:i_src EQUALS STRING:i_dest AliasesList{:
                                                  replace(i_src,i_dest);
                                                :}
  | IDENT:i_src EQUALS IDENT:i_dest             {:
                                                  replace(i_src,i_dest);
                                                :}
  | IDENT:i_src EQUALS STRING:i_dest            {:
                                                  replace(i_src,i_dest);
                                                :}
  | IDENT:i_src ARROW IDENT:i_dest AliasesList {:
                                                  replace(i_src,i_dest);
                                                :}
  | IDENT:i_src ARROW STRING:i_dest AliasesList{:
                                                  replace(i_src,i_dest);
                                                :}
  | IDENT:i_src ARROW IDENT:i_dest             {:
                                                  replace(i_src,i_dest);
                                                :}
  | IDENT:i_src ARROW STRING:i_dest            {:
                                                  replace(i_src,i_dest);
                                                :}
  ;
