Partage
  • Partager sur Facebook
  • Partager sur Twitter

Probleme avec calculette (analyse lexicale)

    9 février 2023 à 12:43:41

    salut, premiere fois que je demande de l'aide ici, je ne sais pas si j'explique comme il faut

    je dois faire une calculatrice et le probleme vient de mon fichier analyse_lexicale, en fait pour la plupart des calculs, il renvoie les bons résultats, mais dans certains cas ca ne fonctionne pas, comme le calcul (25-(5*2))  il va faire le calcul dans la paranthese (5*2)=10 mais ne va pas faire 25-(10) par la suite  et je n'arrive pas a savoir pourquoi ca fait des heures, et je suis totalement désespéré 😞 

    #include <stdio.h>
    #include <stdlib.h>
    #include "analyse_lexicale.h"
    #include "analyse_syntaxique.h"
    #include "lecture_caracteres.h"
    
    typedef enum {Q0, Q1, Q2} Etat ;
    
    Lexeme lc ; // Lexeme courant (1+1)
    
    void Rec_eaep(int  op, int * resultat){
      printf("JE SUIS REC EAEP\n");
      lc = lexeme_courant();
      int operande = -1;
      if(! fin_de_sequence()){
        switch (lc.nature){
          case ENTIER:
            printf("JE SUIS UN ENTIER %d \n", lc.valeur);
            switch(op){
              case 0:
                *resultat = lc.valeur;
                break;
              case 1:
                //+
                *resultat = *resultat + lc.valeur;
                break;
              case 2:
                //-
                *resultat = *resultat - lc.valeur;
                break;
              
              case 3:
                //x
                *resultat = *resultat * lc.valeur;
                break;
              
              case 4:
                // DIVISION
                *resultat = *resultat / lc.valeur;
                break;
            }
            avancer();
            break;
          case PARF:
            printf("Je suis une parenthese fermee\n");
            avancer();
            operande = Rec_op();
            Rec_eaep(operande, resultat);
            break;
          case PARO:
            avancer();
            printf("JE SUIS PARO\n");
            Rec_eaep(0, resultat);
            operande = Rec_op();
            Rec_eaep(operande, resultat);
            printf("\n");
            if(lc.nature == PARF){
              avancer();
              printf("Je suis une parenthese fermee\n");
            }
            break;
          default: 
            printf("Erreur, Rec_eaep\n");
        }
      }
    }
    
    int Rec_op(){
      lc = lexeme_courant();
      printf("JE SUIS REC OP\n");
      if(! fin_de_sequence()){
        switch (lc.nature){
          case PLUS:
            printf("Opération +\n");
            avancer();
            return 1;
            break;
          case MOINS:
            printf("Opération -\n");
            avancer();
            return 2;
            break;
          case MUL:
            printf("Opération *\n");
            avancer();
            return 3;
            break;
          case DIV:
            printf("Opération /\n");
            avancer();
            return 4;
            break;
          default:
            printf("Error, Rec_op\n");
        }
      }
      
      return -1;
     }
    
    void analyser(char *nom_fichier, int * resultat){
        demarrer(nom_fichier);
        int position = 0;
        int op; //Add, sous...
        int res;
        int p;
        Etat ec ;  // etat courant
        ec = Q0 ; //etat initial 
        int a,b; //valeur entiere
        while (! fin_de_sequence()) {
            lc = lexeme_courant();
            switch (ec) {
                case Q0:
                    switch(lc.nature) {
                        case ENTIER:
                            ec = Q1;
                            a = lc.valeur;
                            p = a;
                            break;
                            
                        case PARO:
                            Rec_eaep(0, &a);
                            ec = Q1;
                            printf("Resultat output: %d \n", a);
                            res = a;
                            break;
                        default: 
                            erreur_syntaxique();
                            break;
                    }
                    break;
                case Q1:
                    switch(lc.nature) {
                        case PLUS:
                            ec = Q2;
                            op = 0;
                            break;
                        case MOINS:
                            ec = Q2;
                            op = 1;
                            break;
                        case MUL:
                            ec = Q2;
                            op = 2;
                            break;
                        case DIV:
                            ec = Q2;
                            op = 3;
                            break;
                        case PARO:
                          ec = Q0;
                          op = 4;
                          
                        
                        default: 
                            erreur_syntaxique();
                            break;
                    }
                    break;
                case Q2:
                
                    switch (lc.nature) {
                        case ENTIER: 
                            b = lc.valeur;
                            ec = Q1;
                            switch(op){
                              case 0:
                                a = a+b;
                                printf("res %d\n", a);
                                break;
                              case 1:
                                a = a-b;
                                printf("res %d\n", a);
                                break;
                              case 2:
                                a = a*b;
                                printf("res %d\n", a);
                                break;
                              case 3:
                                a = a/b;
                                printf("res %d\n", a);
                                break;
                            }
                            res = a;
                            break; 
                        case PARO:
                        Rec_eaep(0, &b);
                        a = a-b;
                        printf("res %d\n", a);
                          switch(op){
                              case 0:
                                a = a+b;
                                printf("res %d\n", a);
                                break;
                              case 1:
                                a = a-b;
                                printf("res %d\n", a);
                                break;
                              case 2:
                                a = a*b;
                                printf("res %d\n", a);
                                break;
                              case 3:
                                a = a/b;
                                printf("res %d\n", a);
                                break;
                                }
                            res = a;
                            break;
    
                        default: 
                            erreur_syntaxique();
                            break;
                    }
                    break ;
                default:
                    printf("ok.");
                    break;
                    erreur_syntaxique();
            }
            avancer();
            position++;
        }
    
      
        if (ec == Q1){
            printf("Syntaxe correcte !\n");
            printf("Le resultat du calcul: %d\n", res);
        }
        else{
            printf("Erreur !");
        }
    }
    
    void erreur_syntaxique(){
        printf("Error, Erreur de syntaxe !");
        exit(0);
    }
    



    -
    Edité par okdac45 9 février 2023 à 12:47:14

    • Partager sur Facebook
    • Partager sur Twitter
      9 février 2023 à 13:39:25

      On a pas de boule de crystal, si tu n'as pas d'erreur on va pas se taper tout l'algorithme et le code lié.

      Il existe les debugger pour ce genre de travail, tu dois pouvoir mieux que nous savoir où placer les points d'arrêt afin d'analyser plus en détails ce qui ne va pas.

      • Partager sur Facebook
      • Partager sur Twitter

      Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
      La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

        9 février 2023 à 13:54:18

        Lexeme lc ; // Lexeme courant (1+1)
         
        void Rec_eaep(int  op, int * resultat){
          printf("JE SUIS REC EAEP\n");
          lc = lexeme_courant();
          int operande = -1;
          if(! fin_de_sequence()){
            switch (lc.nature){
              case ENTIER:
                printf("JE SUIS UN ENTIER %d \n", lc.valeur);
                switch(op){
                  case 0:
                    *resultat = lc.valeur;
                    break;
                  case 1:
                    //+
                    *resultat = *resultat + lc.valeur;
                    break;
                  case 2:
                    //-
                    *resultat = *resultat - lc.valeur;
                    break;
                   
                  case 3:
                    //x
                    *resultat = *resultat * lc.valeur;
                    break;
                   
                  case 4:
                    // DIVISION
                    *resultat = *resultat / lc.valeur;
                    break;
                }
                avancer();
                break;
              case PARF:
                printf("Je suis une parenthese fermee\n");
                avancer();
                operande = Rec_op();
                Rec_eaep(operande, resultat);
                break;
              case PARO:
                avancer();
                printf("JE SUIS PARO\n");
                Rec_eaep(0, resultat);
                operande = Rec_op();
                Rec_eaep(operande, resultat);
                printf("\n");
                if(lc.nature == PARF){
                  avancer();
                  printf("Je suis une parenthese fermee\n");
                }
                break;
              default:
                printf("Erreur, Rec_eaep\n");
            }
          }
        }
        Je pense que le problème vient de cette partie, pour le cas (25-(5*2)) il va me donner comme résultat 10 il ne fait pas pas le 25-10
        • Partager sur Facebook
        • Partager sur Twitter
          9 février 2023 à 13:57:38

          Ton code n'est pas testable, il y a des variables qu'on ne connaît pas... En plus il te faudrait reproduire l'erreur sur un petit code C pour te rendre compte de la problématique.
          • Partager sur Facebook
          • Partager sur Twitter

          Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
          La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

            9 février 2023 à 16:11:15

            Pour faire une analyse lexicale, il faut commencer par bien poser le problème. J'ai l'impression que la manière que tu utilises t'amènes à une impossibilité. C'est la raison principale qui fait qu'on ne peut pas analyser ton code.

            Pour calculer une expression:
            - on calcule au moment où on a les données nécessaires; donc dès qu'on a les 2 opérandes et l'opérateur.
            - on sait que l'on a des données nécessaires quand on vient de lire une valeur (sauf peut-être la 1ère) ou on vient de lire une parenthèse fermante.
            - quand on voit une parenthèse ouvrante, on va chercher la valeur par récursivité. La fonction appelée alors un seul but retourner une valeur, elle s'arrête quand elle rencontre une fin de texte ou une parenthèse fermante.

            Le cas de la première valeurs (le s est volontaire!), est simple ou complexe à gérer. Il faut y penser.
            Si on ne trouve jamais la parenthèse fermante, ça peut être aussi un piège à gérer.

            -
            Edité par Dalfab 9 février 2023 à 16:15:52

            • Partager sur Facebook
            • Partager sur Twitter

            En recherche d'emploi.

              9 février 2023 à 17:32:01

              > Je pense que le problème vient de cette partie

              Quand celui qui a écrit le programme ne trouve pas pas l'erreur dans son code, c'est qu'il cherche l'erreur obstinément là où elle n'est pas, et refuse de regarder le reste (*).  Sinon il aurait déjà trouvé.

              C'est d'ailleurs pour ça qu'on demande à quelqu'un d'autre d'y regarder : il ne va pas avoir les même a prioris sur ce qui ne va pas, à condition de rester sourd aux déclarations du genre "nan nan, ça vient d'autre chose, ça je suis sûr que ça marche, c'est pas la peine que tu regardes". (**)

              Commençons par le début : vérifier que l'analyse lexicale se fait correctement. Pour ça, une boucle du genre

              démarrer analyseur
              tant que il y des lexèmes:
                  faire afficher le lexème courant
                  avancer au suivant
              
              

              ça permet de vérifier.

              > dans certains cas ça ne fonctionne pas, comme le calcul (25-(5*2))

              C'est une bonne nouvelle en fait. Avec un exemple bien identifié, il suffit de suivre "à la main" ce que ton programme en fait.

              Conseil : chercher avant si il y a d'autres exemples plus petits :-)

              A ce propos, il faudrait que tu précises la syntaxe des expressions que tu veux évaluer.  Sont elles complètement parenthésées, où y a t-il des priorités d'opérateurs ?

              ----

              Tiens ça me rappelle un truc https://www.mbillaud.fr/SITE-PERSO-LABRI/Resources/resources/Langages2014/notes-de-cours-langages.pdf  p 46 et suivante, sur la technique de descente recursive.

              La grammaire classique E T F (Expressions Termes Facteurs) conduit à 3 fonctions mutuellement récursives.

               C'est du C++, qui se traduit facilement en C ensuite.

               Et aussi : http://michel.billaud3.free.fr/blog/index.php/2013/01/20/72-programmation-la-technique-de-descente-recursive

              (*) c'est du vécu.

              (**) la psychologie de la programmation, c'est fun.

              -
              Edité par michelbillaud 9 février 2023 à 17:57:18

              • Partager sur Facebook
              • Partager sur Twitter
                9 février 2023 à 17:51:37

                Ça se fait aussi avec des arbres binaires, autant pour l'analyse que pour l'évaluation, mais c'est plus complexe.

                S'il n'y a pas d'erreur dans l'expression, on peut jeter un coup d'oeil à ceci:

                https://openclassrooms.com/forum/sujet/baisser-lutilisation-memoire-de-mon-code

                -
                Edité par PierrotLeFou 9 février 2023 à 18:14:47

                • Partager sur Facebook
                • Partager sur Twitter

                Le Tout est souvent plus grand que la somme de ses parties.

                  9 février 2023 à 18:18:05

                  Un truc louche, c'est les appels à lc = lexeme_courant();

                  dont on ne sait pas trop ce qu'ils font, vu qu'on à déjà accès au lexème courant dans une variable globale (que avancer() fait évoluer, on suppose, et dont findesequence fait quelque chose, peut être.).

                  La bizarritude, c'est que cet appel est fait à chaque entrée dans la fonction récursive au nom incompréhensible.

                  Conseil au passage: ajouter un lexeme FIN

                  Aussi: là on est dans l'analyse syntaxique, pas lexicale (découpage en lexèmes successifs). L'analyse syntaxique, ça s'occupe de construire un résultat à partir des lexèmes.

                  ---

                  La syntaxe des expressions, c'est quelque chose comme ça ?

                  expr ::=  nombre
                        |   "("  expr  opération expr ")"
                  
                  opération ::=  "+" | "-" | "*" | "/"
                  

                  et donc

                  ligne ::=   expr  FIN


                  ?

                  (Indication subliminale : amorcer l'analyseur lexical au début de la ligne, pas pour chaque expression).

                  -
                  Edité par michelbillaud 9 février 2023 à 18:29:56

                  • Partager sur Facebook
                  • Partager sur Twitter
                    9 février 2023 à 18:30:57

                    Est-ce qu'on tient compte des opérateurs qui sont parfois "unaires" comme le '-'
                    Est-ce que  --4  est valide? Ou si on doit écrire  -(-4)

                    Est-ce que la grammaire classique E T F tient compte des opérateurs de différents types et priorité:
                    ** : exposant (emprunté de Python)
                    * / %
                    + -
                    < <= == != > >=
                    && || !
                    & | ^ ~

                    -
                    Edité par PierrotLeFou 9 février 2023 à 19:08:13

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Le Tout est souvent plus grand que la somme de ses parties.

                      9 février 2023 à 19:35:36

                      @Pierrot: d'après le (peu de) code montré, cette calculatrice n'est prévue que pour les 4 opérations élémentaires, mais en incluant les parenthèses.

                      J'avais écrit un code de ce genre il y a bien longtemps pour m'amuser (uniquement pour les entiers), qui fonctionnait plus ou moins comme la npi. J'avais un tableau d'entiers pour les opérandes et un tableau pour les opérateurs.

                      Ainsi, 3+4-2*6 se traduisait par push 3, push '+', push 4, je trouve le '-' (priorité identique à '+'), on exécute 3+4, on pop le 3, le 4 et le '+' des 2 tableaux, on push le résultat (7), on push ensuite le '-' . On push alors le 2 qu'on trouve ensuite, puis on push le '*' qui a priorité > '-', ensuite on push le 6, et on tombe sur la fin de l'input. Il reste donc a effectuer 2*6 (on jette  [pop] donc 2 , 6 et '*') et on push le résultat (12). Et comme nous sommes arrivés à la fin de l'instruction, on calcule 7-12 (-5), on pop le 7, le 12 et le '-', et on push -5. Et bingo.

                      -
                      Edité par edgarjacobs 9 février 2023 à 19:41:04

                      • Partager sur Facebook
                      • Partager sur Twitter

                      On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

                        9 février 2023 à 22:46:12

                        Salut excusez moi j ai fait ce post  un peu désespéré a la va vite vers la pause entre deux cours  après avoir passé 4h30 dessus et très peu dormi, du coup il est vrai que j ai pas assez explicité,  je vais essayé de revoir le code demain matin en prenant compte de vos réponses 👍
                        • Partager sur Facebook
                        • Partager sur Twitter
                          9 février 2023 à 23:05:32

                          > Est-ce que la grammaire classique E T F tient compte des opérateurs de différents types et priorité ?

                          Oui, mais la célèbre ETF c'est juste 3 niveaux

                          expression ::=   terme
                                       |   expression op-additif terme
                          
                          terme      :=    facteur
                                       |   terme  op-multiplicatif facteur
                          
                          facteur    :=   constante
                                       |  variable
                                       |  "("  expression ")"
                          
                          


                          mais ça peut servir de modèle pour plus compliqué. Par exemple les 15 niveaux de précédence de C (bon courage).

                          Si tu veux juste rajouter les puissances par exemple, ça doit se caser entre terme et facteur.


                          > passé 4 heures 30  dessus

                          C'est un problème qui mérite bien ça. Le souci des débutants, c'est qu'ils ne se rendent pas compte que la programmation, ça prend beaucoup de temps, surtout au début. Alors ils commencent trop tard, et paf.

                          -----

                          En fait, l'exercice avec les expressions complètement parenthésées peut être traité facilement de façon itérative : intuitivement, on parcourt de gauche à droite,

                          • quand on rencontre un nombre ou un opérateur on les met de côté (dans une/des piles)
                          • et quand on rencontre une parenthèse fermante on combine les deux nombres mis de côté grâce au dernier opérateur, et on met de côté le résultat.
                          On s'aperçoit au passage que les parenthèses ouvrantes ne servent à rien dans cette histoire, elles sont là juste pour faire joli.
                          Si on part d'une ligne déjà découpée en lexèmes, ça ressemble à ça
                          int valeur(const char * lexemes[])
                          {
                              const char *pile_ops[LIMITE];
                              int pile_vals[LIMITE];
                              int nb_ops = 0, nb_vals = 0;
                          
                              for (int i = 0; lexemes[i] != NULL; i++) {
                                  const char *lex = lexemes[i];  // pour abréger
                                  if (est_operateur(lex)) {
                                      pile_ops[nb_ops++] = lex;  // on empile l'opérateur
                                  } else if (est_nombre(lex)) {
                                      pile_vals[nb_vals++] = atoi(lex); // on empile le nombre
                                  } else if (egal(lex, ")")) {
                                      // on combine les deux derniers nombres avec le dernier opérateur
                                      assert(nb_vals >= 2);
                                      assert(nb_ops >= 1);
                                      int b = pile_vals[--nb_vals];
                                      int a = pile_vals[--nb_vals];
                                      const char *op = pile_ops[--nb_ops];
                                      pile_vals[nb_vals++] = combinaison(a, op, b);
                                  } else {
                                      assert ("parenthèse ouvrante attendue" && egal(lex, "("));
                                  }
                              }
                              assert("il reste des opérateurs dans la pile" && nb_ops == 0);
                              assert("il reste trop de valeurs dans la pile"&& nb_vals == 1);
                              return pile_vals[0];
                          }
                          Les fonctions appelées sont laissées en exercice (je fais pas les devoirs)

                          On peut tester avec ça

                          void test(const char * lexemes[], int attendu)
                          {
                              printf("# Evaluation de ");
                              for (int i=0; lexemes[i] != NULL; i++) {
                                  printf("%s ", lexemes[i]);
                              }
                              printf("\n- résultat attendu = %d\n", attendu);
                          
                              int obtenu = valeur(lexemes);
                              printf("- résultat obtenu  = %d\n", obtenu);
                          
                              if (obtenu != attendu) {
                                  printf("*** ERREUR ***\n");
                              }
                          }
                          
                          int main()
                          {
                              test( (const char*[]) {
                                  "123", NULL
                              }, 123);
                              test( (const char*[]) {
                                  "(", "12", "+", "34", ")", NULL
                              }, 46);
                              test((const char *[]) {
                                  "(", "25", "-", "(", "5", "*", "2", ")", ")", NULL
                              }, 15);
                          }
                          
                          et ça affiche ce qu'on veut
                          cc -std=c17 -Wall -Wextra -pedantic -Werror -Wno-unused -D_XOPEN_SOURCE=700 -g    prog.c   -o prog
                          ./prog
                          # Evaluation de 123 
                          - résultat attendu = 123
                          - résultat obtenu  = 123
                          # Evaluation de ( 12 + 34 ) 
                          - résultat attendu = 46
                          - résultat obtenu  = 46
                          # Evaluation de ( 25 - ( 5 * 2 ) ) 
                          - résultat attendu = 15
                          - résultat obtenu  = 15
                          Note : ça ne fait pas le contrôle de la syntaxe, qui est une autre histoire. Il faudrait y ajouter des règles comme
                          • une parenthèse ouvrante est suivie d'une parenthèse ouvrant ou d'un nombre
                          • une parenthèse fermante est suivie d'une parenthèse fermante ou d'un opérateur ou de la fin
                          • un opérateur est suivi d'un nombre ou d'une parenthèse fermante
                          • un nombre est suivi d'un opérateur ou d'une parenthèse fermante ou de la fin
                          • etc
                          qu'on peut vérifier avec un automate + la vérification des niveaux de parenthèses.
                          J'en profite pour prêcher une fois de plus pour les tests unitaires : ici ça m'a permis de voir de suite que je m'étais gourré dans le dépilage de a et b pour les combiner.

                          -
                          Edité par michelbillaud 10 février 2023 à 12:19:51

                          • Partager sur Facebook
                          • Partager sur Twitter

                          Probleme avec calculette (analyse lexicale)

                          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                          • Editeur
                          • Markdown