package GenRGenS.ratexpr;

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

/*********************************************************
 *         Fichiers de descriptions "RATIONAL"           *
 *********************************************************/

/* 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 RatExpr getRatExpr() {
  	return action_obj.getRatExpr();
  }

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

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

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

  public BigDecimal getPoids(Object o)
  { return action_obj.getPoids(o); }


  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;
    
   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
{:
  //Contient toutes les lettres du langage associees a un numero unique
  private HashMap _lettre2int = new HashMap();
  private int _index = 0;

  //Tableau contenant toutes les lettres du langage
  private String[] _lexique;

  private boolean _isWord = true;

  // représentation de l'expression rationelle
  RatExpr _rat;

  //Vaut true ssi le langage utilise est Prosite
  private boolean _isProsite;

  // Variables relatives au stockage des poids lus
  private BigDecimal[] _poids;

  private final String[] LEXIQUE_PROSITE = 
  {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S",
  "T","U","V","W","X","Y","Z"};
  private RatExpr _caractere_indifferent = null;

  public boolean isWord()
  { return _isWord; }

  public String[] getLexique()
  { if (_lexique == null) {
  		initLexique();
  	}
  	return _lexique;
  }
  
  public void initLexique() {
  	_lexique = new String[_index];
  	for(Iterator it = _lettre2int.keySet().iterator(); it.hasNext();) {
  		String lettre = (String) it.next();
  		_lexique[((Integer)_lettre2int.get(lettre)).intValue()] = lettre;
  	}
  }
  
  public RatExpr getRatExpr()
  { return _rat; }

  public boolean isProsite() { return _isProsite; }

  public BigDecimal[] getPoids()
  { return _poids; }
  
  public BigDecimal getPoids(Object o) {
  	if (o instanceof String) {
  		if (_lettre2int.containsKey(o.toString())) {
  			if ((Integer) _lettre2int.get(o.toString()) != null) {
  				Integer i = (Integer) _lettre2int.get(o.toString());
  				int ii = i.intValue();
  				BigDecimal b = _poids[ii];
  				return b;
	  		}
	  		else
	  			System.out.println((Integer) _lettre2int.get(o.toString()));
	  	}
	  	else
	  	{
        //System.out.println(o+" pas dans les poids");
        return new BigDecimal(1);
      }
	}
	if (o instanceof Integer) {
  		int i = ((Integer) o).intValue();
  		if (i < _poids.length && i >= 0)
  			return _poids[i];
  		else
  			if (i != -1) 
        {
  	//i == 1 correspond au cas de la lettre inutile en debut de mot (prosite)
  			//	System.out.println(o+" pas dans les poids");
          return new BigDecimal(1);
        }
	  }
    return new BigDecimal(1);
  }

  //initialise le tableau des poids
	private void initPoids() {
  		_poids = new BigDecimal[_index];
  		for(int i = 0 ; i < _index ; i++)
  			_poids[i] = new BigDecimal(1);
  	}

  /*
    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 (_poids == null)
      	initPoids();
      _poids[i.intValue()] = b;
    }
    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 (_lexique == null)
				initLexique();
			_lexique[i.intValue()] = dest;
		}
		else {
			parser.done_parsing();
			parser.report_error("Attempting to alias an undefined \""+
			src +"\" symbol !","");
    	}
	}
	
/** initialise les attributs pour Prosite */
private void initProsite() {
	_isProsite = true;
	_lexique = new String[LEXIQUE_PROSITE.length];
	for(int i = 0 ; i < LEXIQUE_PROSITE.length ; i++) {
		_lexique[_index] = LEXIQUE_PROSITE[i];
		_lettre2int.put(LEXIQUE_PROSITE[i],new Integer(_index));
		_index++;
	}
}

/** Creation de l'expression rationnelle associee a une lettre */
private RatExpr lettre(String s) {

	//Mode WORDS avec un mot de plus de 1 caractere, donc s ne peut pas etre
	//un symbole valide.
	if (_isWord && s.length()!=1){
		parser.done_parsing();
		parser.report_error(s + " is not a letter","");
		return (new RatExprTerm(null));
	}

	//Mode LETTERS : s est une concatenation de symboles.
	if (!_isWord) {
		if(s.compareTo("x") == 0)
			return(caractere_indifferent());
		RatExpr res;
		String lettre = s.substring(0,1);
		if (_lettre2int.containsKey(lettre)) {
			//System.out.println(lettre);
			res = new RatExprTerm(_lettre2int.get(lettre));
			for(int i = 1 ; i < s.length() ; i++) {
				//System.out.println(lettre);
				lettre = s.substring(i,i+1);
				if (_lettre2int.containsKey(lettre))
					res = new RatExprPoint(res,new RatExprTerm(
						_lettre2int.get(lettre)));
				else {
					parser.done_parsing();
					parser.report_error(s.charAt(i) + " is not a letter","");
					res = new RatExprTerm(null);	
				}
			}
		}
		else {
			parser.done_parsing();
			parser.report_error(s.charAt(0) + " is not a letter","");
			res = new RatExprTerm(null);	
		}
		return res;
	}
	
	//Mode WORDS : s est de taille 1.
	if(s.compareTo("x") == 0)
		return(caractere_indifferent());
	else {
		if (_lettre2int.containsKey(s))
			return (new RatExprTerm(_lettre2int.get(s)));
		else {
			parser.done_parsing();
			parser.report_error(s + " is not a letter","");
			return (new RatExprTerm(null));
		}
	}
}

private RatExpr ambiguite_inclusive(String s) {
	RatExpr e;
	if (_lettre2int.containsKey(s.substring(0,1))) {
		e = new RatExprTerm(_lettre2int.get(s.substring(0,1)));
		for(int i=1; i<s.length(); i++){
			if (_lettre2int.containsKey(s.substring(i,i+1)))
				e = new RatExprOu(e, new RatExprTerm(_lettre2int.get(
					s.substring(i,i+1))));
			else {
				parser.done_parsing();
				parser.report_error(s.charAt(i) + " is not a letter","");
				return (new RatExprTerm(null));
			}
		}
	}
	else {
		parser.done_parsing();
		parser.report_error(s.charAt(0) + " is not a letter","");
		e = new RatExprTerm(null);
	}
	return e;
}


private RatExpr ambiguite_exclusive(String s) {
	//verification de l'integrite de s
	for(int i = 0 ; i < s.length() ; i++) {
		if (!_lettre2int.containsKey(s.substring(i,i+1))) {
			parser.done_parsing();
			parser.report_error(s.charAt(i) + " is not a letter","");
			return (new RatExprTerm(null));
		}
	}
	
	RatExpr e = null;
	for(int i=0; i<LEXIQUE_PROSITE.length; i++){
		if(s.indexOf(LEXIQUE_PROSITE[i])==-1) {
			if (e == null)
				e = new RatExprTerm(_lettre2int.get(LEXIQUE_PROSITE[i]));
			else
				e = new RatExprOu(e, new RatExprTerm(_lettre2int.get(
					LEXIQUE_PROSITE[i])));
		}
	}

	return e;
}

private RatExpr caractere_indifferent() {
	if (_caractere_indifferent == null) {
		_caractere_indifferent = new RatExprTerm(_lettre2int.get(
			LEXIQUE_PROSITE[0]));
		for(int i=1; i<LEXIQUE_PROSITE.length; i++)
			_caractere_indifferent = new RatExprOu(_caractere_indifferent,
				new RatExprTerm(_lettre2int.get(LEXIQUE_PROSITE[i])));
	}
		
	return _caractere_indifferent;
}

private RatExpr insertions_min_max(RatExpr e, BigDecimal b1, BigDecimal b2) {
	if (b1.compareTo(b2)==1)
		return(new RatExprTerm(null));
	
	RatExpr res = new RatExprPuiss(e, b1);
	for(int i=b1.intValue()+1; i<= b2.intValue(); i++)
		res = new RatExprOu(res, new RatExprPuiss(e, i));
	return res;
}

:}


/* Declaration des  terminaux */

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

  /* Mots-clés généraux */
    terminal           TYPE,LANGUAGE,GRAMMAR,MARKOV,RATIONAL,PROSITE,MASTERFILE;
    terminal           SYMBOLS, WORDS, LETTERS;
    terminal           EQUALS, SEMI, VIRG, 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 "rationelle"" */
    terminal           OU, POINT, TIRET, PLUS, ETOILE, PUISS, EXPRESSION,
                       PARG, PARD, ACCG, ACCD, CROG, CROD;
    
  /* 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         RationalDescription, RationalDescriptionSuite;
non terminal         TypeDeclaration, LangageRationalDeclaration,
                     LangagePrositeDeclaration, SymbolTypeDeclaration;
non terminal         RationalDeclaration, PrositeDeclaration;
non terminal         WeightsDeclaration, WeightsList;
non terminal         AliasesDeclaration, AliasesList;

non terminal BigDecimal   Real;
non terminal RatExpr Expression, ExpressionProsite;


precedence left OU;
precedence left POINT, TIRET;
precedence nonassoc PLUS;
precedence nonassoc ETOILE;

precedence left PUISS;
precedence nonassoc PARG,PARD;
precedence left IDENT,REEL,ENTIER;




/* The grammar */

start with RationalDescription;


RationalDescription ::=
    TypeDeclaration RationalDescriptionSuite {: :}
;
  
TypeDeclaration ::=
    TYPE EQUALS RATIONAL                        {: :}

  ;
  
RationalDescriptionSuite ::=
    LangageRationalDeclaration SymbolTypeDeclaration RationalDeclaration
    WeightsDeclaration AliasesDeclaration {: :}
    
   |LangagePrositeDeclaration SymbolTypeDeclaration PrositeDeclaration
    WeightsDeclaration AliasesDeclaration {: :}

  ;


LangageRationalDeclaration ::=
    LANGUAGE EQUALS RATIONAL                        {: :}

  ;

LangagePrositeDeclaration ::=
    LANGUAGE EQUALS PROSITE                        {: initProsite(); :}

  ;

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

RationalDeclaration ::=
    EXPRESSION EQUALS Expression:e              {: _rat = e; :}
  | EXPRESSION EQUALS {:
              parser.done_parsing();
              parser.report_error("You must define a rational expression !","");
                      :}
  |                   {:
              parser.done_parsing();
              parser.report_error("You must define a rational expression !","");
                      :}
  ;


Expression ::=
    Expression:e1 OU Expression:e2      {: RESULT = new RatExprOu(e1,e2);    :}
  | Expression:e1 POINT Expression:e2   {: RESULT = new RatExprPoint(e1,e2); :}
  | Expression:e ETOILE                 {: RESULT = new RatExprEtoile(e);    :}
  | Expression:e PLUS                   {: RESULT = new RatExprPlus(e);      :}
  | Expression:e PUISS PARG Real:b PARD {: if (b.intValue() != 0)
                                             RESULT = new RatExprPuiss(e,b);
                                           else
                                             RESULT = new RatExprTerm(null);
                                        :}
  | Expression:e PUISS PARG Real:b1 VIRG Real:b2 PARD
  			{:  RatExpr rat = new RatExprPuiss(e,b1.intValue());
				for(int i = b1.intValue()+1;i<=b2.intValue();i++) {
					rat = new RatExprOu(rat,new RatExprPuiss(e,i));
				}
				if (b2.intValue()<b1.intValue())
					RESULT = new RatExprTerm(null);
				else
					RESULT = rat;
			:}
  | PARG Expression:e PARD {: RESULT = e;                       :}
  | IDENT:s
  		{:	
  		    if (_isWord) {
  				if (!_lettre2int.containsKey(s)) {
  					_lettre2int.put(s,new Integer(_index));
  					_index++;
  				}
  				RESULT = new RatExprTerm(_lettre2int.get(s));
  			}
  			else {
  				if (!_lettre2int.containsKey(s.substring(0,1))) {
  					_lettre2int.put(s.substring(0,1),new Integer(_index));
  					_index++;
  				}
  				RatExpr res = new RatExprTerm(_lettre2int.get(
  					s.substring(0,1)));
  				for(int i = 1 ; i < s.length() ; i++) {
  					String lettre = s.substring(i,i+1);
  					if (!_lettre2int.containsKey(lettre)) {
  						_lettre2int.put(lettre,new Integer(_index));
  						_index++;
  					}
  					res = new RatExprPoint(res,new RatExprTerm(
  						_lettre2int.get(lettre)));
  				}
  				RESULT = res;
  			}
  		:}

  ;

PrositeDeclaration ::=
    EXPRESSION EQUALS ExpressionProsite:e {: _rat = e; :}
  | EXPRESSION EQUALS {:
             parser.done_parsing();
             parser.report_error("You must define a rational expression !","");
                      :}
  |                   {:
             parser.done_parsing();
             parser.report_error("You must define a prosite expression !","");
                      :}
  ;


ExpressionProsite ::=
    IDENT:s 	                         {: RESULT = lettre(s);               :}  
  | CROG IDENT:s CROD                    {: RESULT = ambiguite_inclusive(s);  :}
  | ACCG IDENT:s ACCD                    {: RESULT = ambiguite_exclusive(s);  :}
  | ExpressionProsite:e PARG Real:b PARD {: if (b.intValue() != 0)
                                              RESULT = new RatExprPuiss(e,b);
                                            else
                                              RESULT = new RatExprTerm(null);
                                          :}
  | ExpressionProsite:e PARG Real:b1 VIRG Real:b2 PARD
                                     {: RESULT = insertions_min_max(e,b1,b2);:}

  | ExpressionProsite:e1 TIRET ExpressionProsite:e2
                                     {: RESULT = new RatExprPoint(e1,e2);    :}
  | PARG ExpressionProsite:e PARD    {: RESULT = e;                          :}
;


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

WeightsDeclaration ::=
    WEIGHTS EQUALS WeightsList {: if (_poids == null) initPoids(); :}
  |                            {: initPoids();                     :}
  ;

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

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

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

