Package freegressi.parser

Source Code of freegressi.parser.Parser

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    Copyright (C) 2011-2012  Marchand Eric <ricoh51@free.fr>
   
    This file is part of Freegressi.

    Freegressi is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Freegressi is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Freegressi.  If not, see <http://www.gnu.org/licenses/>.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package freegressi.parser;

import freegressi.tableur.Noeud;
import freegressi.tableur.Sym;
import java.util.ArrayList;
import java.util.List;

// @todo Transformer les Double en double dans freegressi.flex
/**
* Un noeud terminal est un Nombre, ou une expression entre parenthèses
* Un noeud non terminal est un opérateur +, -, *, / .
* Les noeuds non terminaux possèdent une priorité, par ex * possède une priorité
* plus forte que +, et ^ possède une priorité plus forte que *.
* Chaque noeud possède un fils gauche et un fils droit
* Un fils gauche est toujours créé seul, c'est le début d'une expression.
* Un opérateur est ajouté comme père d'un fils gauche.
* On ajoute donc toujours un noeud terminal comme fils droit.
* Les nombres sont codés en double (4.9e-324 à 1.7e308).
* Les valeurs sont données à 1e-15 près.
*/
public class Parser {

  /** La liste des noeuds, fournie par le lexer */
  private List<Noeud> liste;
  /** Un index utilisé dans la méthode récursive parse */
  private int indexListe = -1;

  /**
   * Constructeur de la classe Parser
   * @param list : la liste des noeuds fournie par le lexer
   */
  public Parser(List<Noeud> list) {
    liste = list;
  }

  /**
   * Vérifie la cohérence de l'arbre racine
   * @param racine : la racine de l'arbre à vérifier
   * @return null si l'arbre est cohérent, le noeud qui pose problème sinon
   */
  private Noeud verifieCoherence(Noeud racine) {
    // un arbre vide est cohérent
    if (racine == null) {
      return null;
    }

    int type = racine.getType();
    int cat = racine.getCategorie();

    // une erreur du lexer, ne devrait pas survenir ici!
    if (type == Sym.ERROR) {
      //racine.setTexteErreur("Erreur du lexer!");
      racine.setTexteErreur("Caractère interdit!");
      return racine;
    }

    // Une virgule ne peut pas être un noeud
    if (racine.getType() == Sym.VIRGULE){

      //if (racine.getNombreParametres() != Integer.MAX_VALUE && racine.getNombreParametres() != racine.getListeArguments().size()){
        racine.setType(Sym.PARSER_COHERENCE_ERROR);
        racine.setTexteErreur("Aucun paramètre attendu!");
        return racine;
      //}//
    }

    // une feuille est soit un Nombre, une Variable ou une constante ou un paramètre de tendance

    if (type == Sym.NOMBRE || type == Sym.ID || type == Sym.PARAMETRE
            || cat == Sym.CATEGORIE_CONSTANTE ) {
      if (!racine.estFeuille()) {
        if (racine.getFilsD() != null){
          racine = racine.getFilsD();
        }
        racine.setType(Sym.PARSER_COHERENCE_ERROR);
        racine.setTexteErreur("Erreur de syntaxe!");
        return racine;
      }
      return null;
    }

    // * / et ^ possèdent un fils gauche
    if (type == Sym.FOIS || type == Sym.DIVISE || type == Sym.PUIS) {
      if (racine.getFilsG() == null) {
        racine.setType(Sym.PARSER_COHERENCE_ERROR);
        racine.setTexteErreur("Erreur de syntaxe!");
        return racine;
      }
      //return (racine.getFilsG() != null)? null: racine;
    }
    // un opérateur possède un fils droit
    if (cat == Sym.CATEGORIE_OPERATEUR) {
      if (racine.getFilsD() == null) {
        racine.setType(Sym.PARSER_COHERENCE_ERROR);
        racine.setTexteErreur("Erreur de syntaxe!");
        return racine;
      }
    }


    // une fonction possède le bon nombre d'arguments
    // si nombreParametres == 1, il n'y a pas de liste d'arguments
    // l'argument se trouve dans le fils gauche?
    if (cat == Sym.CATEGORIE_FONCTION) {


     
      int np = racine.getNombreParametres(); // nombre de paramètres obligatoires
      int na = racine.getListeArguments() == null? 0 : racine.getListeArguments().size(); // nombre d'arguments
      //System.out.println(racine.getTexteLexer() + " Nombre de paramètres : " + np + "Nombre d'arguments : " + na);
      //racine.getRapport();
      if (np == 0){ // ce cas n'existe pas encore (fonction sans paramètre)

      } else if (np == 1){
        if (racine.getFilsD() == null ){ // ne sert à rien?
         
          racine.setType(Sym.PARSER_COHERENCE_ERROR);
          racine.setTexteErreur("La fonction " + racine.getTexteLexer() +" nécessite 1 argument!");
          return racine;
        }
//        if (na != 0){
//          racine.setType(Sym.PARSER_COHERENCE_ERROR);
//          racine.setTexteErreur("La fonction " + racine.getTexteLexer() +" nécessite 1 seul argument!");
//          return racine;
//        }
      }
      else if (np > 1 && np < Integer.MAX_VALUE) { // nombre limité de paramètres
        if (np != na ) {
          racine.setType(Sym.PARSER_COHERENCE_ERROR);
          racine.setTexteErreur("La fonction " + racine.getTexteLexer() +" nécessite " + np + " argument(s)!");
          return racine;
        }
//        if (racine.getNombreParametres() != racine.getListeArguments().size()) {
//          racine.setType(Sym.PARSER_COHERENCE_ERROR);
//          racine.setTexteErreur("Cette fonction n'a pas le bon nombres d'arguments!");
//          return racine;
//        }

      } else  { // nombre indéfini d'arguments, np == Integer.MAX_VALUE
        if (racine.getNombreParametres() > 1 && racine.getListeArguments() == null) {
          racine.setType(Sym.PARSER_COHERENCE_ERROR);
          racine.setTexteErreur("La fonction " + racine.getTexteLexer() +" nécessite au moins 1 argument!");
          return racine;
        }
      }
      // Vérifier la cohérence des paramètres
      if (na > 0){
        for (int i = 0; i < racine.getListeArguments().size(); i++){
          Noeud argument = verifieCoherence(racine.getListeArguments().get(i));
          if (argument != null){
            return argument;
          }
        }
        // Vérifier la pertinence des paramètres
        Noeud erreur;
        switch (type){
          case Sym.MOYENNE:
          case Sym.RAND:
          // Aucune vérification à faire
            erreur = checkFilsDroit(racine);
            if (erreur != null) return erreur;
            break;

          case Sym.DERIVEE:
          // le deuxième paramètre doit être un nom de variable
            erreur = checkFilsDroit(racine);
            if (erreur != null) return erreur;
            Noeud arg2 = racine.getListeArguments().get(1);
            if (arg2.getType() != Sym.ID){
              arg2.setType(Sym.PARSER_COHERENCE_ERROR);
              arg2.setTexteErreur("La fonction " + racine.getTexteLexer() +
                      " nécessite une variable comme deuxième argument!");
              return arg2;
            }
            break;
          // retirée du lexer
          case Sym.INTEGRALE:
            erreur = checkFilsDroit(racine);
            if (erreur != null) return erreur;
            // le 4ème paramètre doit être un nom de variable
            Noeud arg4 = racine.getListeArguments().get(3);
            if (arg4.getType() != Sym.ID){
              arg4.setType(Sym.PARSER_COHERENCE_ERROR);
              arg4.setTexteErreur("La fonction " + racine.getTexteLexer() +
                      " nécessite une variable comme 4ème argument!");
              return arg4;
            }
            break;
        }
      }


    }

//    // Le fils gauche du signe égal doit être un identificateur
//    if (type == Sym.EGAL){
//      if (racine.getFilsG() == null || racine.getFilsG().getType() != Sym.ID){
//        racine.setType(Sym.PARSER_COHERENCE_ERROR);
//        racine.setTexteErreur("Erreur : Nom = Expression!");
//        return racine;
//      }
//    }


    // Vérifier la cohérence du fils gauche
    Noeud filsG = verifieCoherence(racine.getFilsG());
    if (filsG != null) {
      return filsG;
    }
    // Vérifier la cohérence du fils droit
    Noeud filsD = verifieCoherence(racine.getFilsD());
    if (filsD != null) {
      return filsD;
    }
    return null;
  }

  /**
   * Vérifie si le fils droit est bien null, utilisé dans la méthode parse
   * @param racine
   * @return
   */
  private Noeud checkFilsDroit(Noeud racine){
    // Cette fonction ne peut avoir de fils droit
    Noeud filsD = racine.getFilsD();
    if (filsD != null){
      filsD.setType(Sym.PARSER_COHERENCE_ERROR);
      filsD.setTexteErreur("Erreur de syntaxe!");
      return filsD;
    }
    return null;
  }

  /**
   * La méthode publique principale, lance le parsing de la liste de noeuds
   * fournie au constructeur.
   * @return
   */
  public Noeud parser() {
    Noeud noeud = parse(0, null);
    if (noeud == null){
      return noeud;
    }
    if (noeud.getType() == Sym.PARSER_COHERENCE_ERROR){
      return noeud;
    }
    Noeud erreur = verifieCoherence(noeud);
    if (erreur != null) {
      noeud = erreur;
    }
    return noeud;
  }

  /**
   * La méthode principale de la classe, elle est chargée de renvoyer
   * un arbre correspondant à la liste de noeuds fournie par le lexer
   * @param index du prochain noeud dans la liste
   * @param parG est le noeud "(" si parse est appelée par une parenthèse ouvrante "("
   * @return la racine de l'arbre
   */
  private Noeud parse(int index, Noeud parG) {
    Noeud noeud = getNoeudSuivant();
    Noeud nCourant = null;
    boolean attendArgument = false;
    while (noeud != null) {

      switch (noeud.getType()) {
        case Sym.ERROR:
          noeud.setTexteErreur("Erreur du lexer!");
          return noeud;
         
        case Sym.UNIT:
        case Sym.COMMENT:
        case Sym.COMMENT_TEXT:
        case Sym.UNIT_TEXT:
          attendArgument = false;
          //nCourant = ajouteNoeudTerminal(noeud, nCourant);
          break;
        case Sym.EGAL:
          attendArgument = false;
          nCourant = ajouteNoeudNonTerminal(noeud, nCourant);
          break;
        case Sym.NOMBRE:
          attendArgument = false;
          nCourant = ajouteNoeudTerminal(noeud, nCourant);
          //System.out.print("N");
          break;

        case Sym.PLUS:
        case Sym.MOINS:
        case Sym.FOIS:
        case Sym.DIVISE:
          attendArgument = false;
          nCourant = ajouteNoeudNonTerminal(noeud, nCourant);
          break;

        case Sym.VIRGULE:
          //System.out.println("Virgule : " + noeud.getTexteLexer());
          if (attendArgument){
            nCourant.setType(Sym.PARSER_COHERENCE_ERROR);
            nCourant.setTexteErreur("Attente d'un argument");
            return nCourant;           
          }
          attendArgument = true;
          nCourant = ajouteNoeudNonTerminal(noeud, nCourant);
          break;

        case Sym.PUIS:
          attendArgument = false;
          nCourant = ajouteNoeudNonTerminal(noeud, nCourant);
          //System.out.print("^");
          break;
        case Sym.PUIS2:
          attendArgument = false;
          Noeud n = new Noeud(Sym.NOMBRE, Sym.CATEGORIE_SANS, Sym.PRIORITE_BASSE, 2.0, noeud.getLigne(), noeud.getColonne(), "");
          noeud.setType(Sym.PUIS);
          nCourant = ajouteNoeudNonTerminal(noeud, nCourant); // ajout du noeud PUIS
          nCourant = ajouteNoeudTerminal(n, nCourant); // ajout du noeud NOMBRE : 2
          //System.out.print("^2");
          break;
        case Sym.PUISN:
          attendArgument = false;
          Noeud n1 = new Noeud(Sym.NOMBRE, Sym.CATEGORIE_SANS, Sym.PRIORITE_BASSE, (double) noeud.getPuissance(), noeud.getLigne(), noeud.getColonne(), "");
          //System.out.println(noeud.getPuissance());
          noeud.setType(Sym.PUIS);
          noeud.setCategorie(Sym.CATEGORIE_OPERATEUR);
          noeud.setPriorite(Sym.PRIORITE_HAUTE);
          nCourant = ajouteNoeudNonTerminal(noeud, nCourant); // ajout du noeud PUIS
          nCourant = ajouteNoeudTerminal(n1, nCourant); // ajout du noeud NOMBRE : N
          //System.out.print("^2");
          break;
        case Sym.ID:
        case Sym.PARAMETRE:
          attendArgument = false;
          nCourant = ajouteNoeudTerminal(noeud, nCourant);
          //System.out.print("Id : "+noeud.getNom());
          break;

        case Sym.SIN:
        case Sym.COS:
        case Sym.TAN:
        case Sym.ASIN:
        case Sym.ACOS:
        case Sym.ATAN:
          attendArgument = false;
          nCourant = ajouteNoeudTerminal(noeud, nCourant);
          break;

        case Sym.LOG:
        case Sym.LN:
        case Sym.EXP:
        case Sym.SQR:
        case Sym.SQRT:
        case Sym.ABS:
          attendArgument = false;
          nCourant = ajouteNoeudTerminal(noeud, nCourant);
          break;

        case Sym.PI:
        case Sym.E:
          attendArgument = false;
          nCourant = ajouteNoeudTerminal(noeud, nCourant);
          //System.out.print("ATAN");
          break;

        case Sym.MOYENNE:
          attendArgument = false;
          nCourant = ajouteNoeudTerminal(noeud, nCourant);
          //System.out.print("ATAN");
          break;
        case Sym.DERIVEE:
        case Sym.INTEGRALE:
        case Sym.RAND:
          attendArgument = false;
          nCourant = ajouteNoeudTerminal(noeud, nCourant);
          break;


        case Sym.PARENG:
          //attendArgument = true;
          noeud = parse(indexListe + 1, noeud);

          if (noeud != null && noeud.getType() == Sym.PARSER_COHERENCE_ERROR) {
            return noeud;
          }
          // Mettre les arguments de fonction sous forme de liste
          if (nCourant != null && nCourant.getCategorie() == Sym.CATEGORIE_FONCTION) {
            if (nCourant.getNombreParametres() > 1) { // uniquement pour les fonctions à plusieurs paramètres
              List<Noeud> listeP = new ArrayList<Noeud>();
              int nombreParametres = getListeParametres(noeud, listeP);
              if (nCourant.getNombreParametres() == Integer.MAX_VALUE || nombreParametres == nCourant.getNombreParametres()) {
                ajouteNoeudTerminalArguments(listeP, nCourant);
                break;
              } else {
                nCourant.setType(Sym.PARSER_COHERENCE_ERROR);
                nCourant.setTexteErreur("Cette fonction nécessite " + nCourant.getNombreParametres() + " arguments!");
                return nCourant;
              }
            } else { // fonction à 1 paramètre, vérifier qu'il n'y en a qu'un
              List<Noeud> listeP = new ArrayList<Noeud>();
              int nombreParametres = getListeParametres(noeud, listeP);
              if (nombreParametres != 1){
                nCourant.setType(Sym.PARSER_COHERENCE_ERROR);
                nCourant.setTexteErreur("Cette fonction nécessite 1 seul argument!");
                return nCourant;
              }
            }
          }
          nCourant = ajouteNoeudTerminal(noeud, nCourant);
          attendArgument = false;
          break;
        case Sym.PAREND:
          if (attendArgument){
            nCourant.setType(Sym.PARSER_COHERENCE_ERROR);
            nCourant.setTexteErreur("Attente d'un argument");
            return nCourant;
          }
         
          if (parG == null) { // ici erreur : on détecte une parenthese droite mais il n'y a pas de gauche
            noeud.setType(Sym.PARSER_COHERENCE_ERROR);
            noeud.setTexteErreur("pas de parenthèse ouvrante associée!");
            return noeud;
          }
          if (nCourant == null) {
            Noeud erreur = new Noeud(Sym.PARSER_COHERENCE_ERROR, Sym.CATEGORIE_SANS, Sym.PRIORITE_HAUTE, 0, parG.getColonne(), "");
            erreur.setTexteErreur("Expression vide!");
            return erreur;
          } else {
            attendArgument = false;
            return nCourant.getRacine();
          }
        //return (nCourant == null)? null : nCourant.getRacine();



        default:
          System.err.println("Erreur interne : Symbole non reconnu dans la méthode \"parse\"");
          noeud.setType(Sym.PARSER_ERROR);
          noeud.setTexteErreur("Symbole non reconnu!");
          return noeud;
      }

      noeud = getNoeudSuivant();
    }
    //return liste.get(index).getRacine();
    if (parG != null) { // on quitte parse sans avoir fermé la parenthese
      parG.setType(Sym.PARSER_COHERENCE_ERROR);
      parG.setTexteErreur("Parenthèse non fermée!");
      return parG;
    }

    return (nCourant == null) ? null : nCourant.getRacine();
  }

  /**
   * Ajoute listArg comme liste d'arguments à noeud
   * @param listArg
   * @param noeud
   */
  private void ajouteNoeudTerminalArguments(List<Noeud> listArg, Noeud noeud) {
    noeud.setListeArguments(listArg);
  }


  /* *************************************************************** */
  //private List<Noeud> listeParametres;
  private void donneParametres(Noeud noeud, List<Noeud> listeParametres) {
    if (noeud.getType() != Sym.VIRGULE) {
      listeParametres.add(noeud);
    } else {
      if (noeud.getFilsG() != null) {
        donneParametres(noeud.getFilsG(), listeParametres);
      }
      if (noeud.getFilsD() != null) {
        donneParametres(noeud.getFilsD(), listeParametres);
      }
    }
  }

  /**
   * Remplit la listeParamètres avec les arguments trouvés dans l'arbre noeud.
   * La liste doit être instanciée par l'appelant.
   * @param noeud : le noeud racine des arguments
   * @param listeParametres : la liste des arguments
   * @return le nombre d'arguments trouvés
   */
  private int getListeParametres(Noeud noeud, List<Noeud> listeParametres) {
    listeParametres.clear();
    if (noeud == null) {
      return 0;
    }
    //if (noeud.getFilsD() == null) return 0;

    donneParametres(noeud, listeParametres);
    return listeParametres.size();
  }

  /* *************************************************************** */
  /**
   * renvoit le noeud suivant dans la liste de noeuds fournie par le lexer
   * @return le noeud suivant dans la liste
   */
  private Noeud getNoeudSuivant() {
    if (liste == null) {
      return null;
    }
    indexListe++;
    if (indexListe < liste.size()) {
      return liste.get(indexListe);
    } else {
      return null;
    }
  }

  /**
   * Ajoute noeud comme fils droit à nCourant
   * @param noeud
   */
  private Noeud ajouteNoeudTerminal(Noeud noeud, Noeud nCourant) {
    if (noeud == null || nCourant == null) {
      return noeud;
    }
    /*if (nCourant.estFilsGaucheLibre()) { // ne sert à rien sauf à détecter des erreurs pendant que je débugge
    nCourant.setFisG(noeud);
    noeud.setPere(nCourant);
    System.err.println("<Parser.ajouteNoeudTerminal> : estFilsGaucheLibre!!");
    }
    else*/
    if (nCourant.estFilsDroitLibre()) {
      nCourant.setFilsD(noeud);
      noeud.setPere(nCourant);
    } else {
      System.err.println("Erreur interne <Parser.ajouteNoeudTerminal>");
    }
    return noeud;
  }

  /**
   * Ajoute le noeud au noeud courant en tenant compte de la priorité
   * @param noeud
   */
  private Noeud ajouteNoeudNonTerminal(Noeud noeud, Noeud nCourant) {
    if (nCourant == null) {
      nCourant = noeud;
    } else {
      Noeud n = getFilsAieulPrioritePlusFaible(noeud.getPriorite(), nCourant);
      insereNoeudAuDessusDe(noeud, n);
    }
    return noeud;
  }

  /**
   * Renvoit le fils de l'aieul de noeud nDepart qui possède une priorité inférieure
   * à celle de noeud
   *
   * @return
   */
  private Noeud getFilsAieulPrioritePlusFaible(int priorite, Noeud nDepart) {
    Noeud n = nDepart;

    while (true) { // pas très joli ça...
      if (n.getPere() == null) { // on arrive à la racine de l'arbre
        return n;
      }
      n = n.getPere();
      if (n.getPriorite() < priorite) { // le pere de n a une priorité plus faible que priorite
        return n.getFilsD();
      }
    }
  }

  /**
   * Insere noeud entre noeudBas et noeudBas.getPere()
   * @param noeud
   */
  private void insereNoeudAuDessusDe(Noeud noeud, Noeud noeudBas) {
    Noeud pere = noeudBas.getPere();
    noeudBas.setPere(noeud);
    noeud.setFisG(noeudBas);
    noeud.setPere(pere);
    if (pere != null) {
      /*if (pere.estFilsGaucheLibre()) {
      pere.setFisG(noeud);
      System.err.println("Passe par estFilsGaucheLibre dans insereNoeudAuDessusDe");
      }
      else {*/
      pere.setFilsD(noeud);
      //}
    }
    //nCourant = noeud;
  }
}
TOP

Related Classes of freegressi.parser.Parser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.