Partage
  • Partager sur Facebook
  • Partager sur Twitter

[FAIT][Défis] #4 : Faisons une vraie calculette !

Venez vous entraîner !

    30 septembre 2011 à 18:44:41

    Bonjour à tous les zéros,

    L'équipe organisatrice a décidé d'augmenter à 2 semaines l'intervalle entre deux défis. Les participants du défi #3 n'ont pas été nombreux, certainement à cause de la difficulté. Cependant, les réponses sont riches d'informations !
    Cette semaine, nous vous proposons un nouveau parser, un parser de calculatrice. Le défi #1 est un jeu d'enfant à côté ! Ne vous inquiétez pas, beaucoup d'informations et d'aides vous sont donnés.

    Si vous souhaitez plus de renseignements sur les défis, rendez vous sur le topic de recensement des défis, vous y trouverez également les règles principales.

    Cet exercice a été écrit par GuilOooo, un grand merci à lui !

    Faisons une vraie calculette !



    Note : cet exercice est inspiré d'un atelier qui fût organiser sur le forum « autres langages ». Vous pouvez aller lire cette discussion et y participer si vous avez quelque chose de nouveau à y ajouter ! Cependant, ce topic comprend plusieurs solutions, dont certaines en langage C. Méfiez-vous ! Je vous conseille d'essayer par vous-mêmes avant de voir comment les autres ont fait. En ayant essayé par vous-mêmes, vous repérerez où sont les difficultés, et vous profiterez d'autant mieux de la lecture de ce topic.

    C'est parti !

    Introduction



    Savez-vous ce qu'est un parser ? Il s'agit d'un bout de code dont le but est d'analyser une chaîne de caractères afin d'en dégager la structure. Par exemple, si je vous donne la chaîne "3+2*(4-3)", un parser pourrait la transformer en arbre, comme ceci :
    Arbre :
        +
      /   \
    3       *
          /   \
        2       -
              /   \
            4       3


    Bien entendu, les parsers peuvent servir à beaucoup de choses, depuis l'analyse d'expressions mathématiques simples jusqu'à l'analyse de codes sources. Le parsing est la première étape qu'effectue votre compilateur de C favori pour produire un exécutable. Il s'agit de l'étape permettant « comprendre » le code. :)

    Les parsers sont des outils très utiles qui vous serviront très probablement à un moment donné. Cependant, cet exercice traitera uniquement d'expressions mathématiques, je vais donc me focaliser là-dessus.

    L'arbre correspondant à l'expression du premier exemple pourrait être représenté par la structure suivante :
    /* Un nœud de l'arbre peut être soit un opérateur (+,-,/,*), soit
       un nombre. On prévoit une énumération pour pouvoir rajouter des cas. */
    enum type
    {
        OPERATEUR,
        NOMBRE
    };
    typedef enum type type_e;
    
    /* La structure qui contient les informations d'un nœud. L'arbre
       tout entier sera représenté par le nœud racine. */
    struct arbre
    {
        type_e        type;   /* Opérateur ou nombre ? */
        int           valeur; /* Valeur du nombre/code ascii de l'opérateur. */
        struct arbre *gauche; /* Pointeur vers le sous-arbre gauche. */
        struct arbre *droit;  /* Idem avec le sous-arbre droit. */
    };
    typedef struct arbre arbre_s;
    /* Bien entendu, dans le cas d'un nombre, les deux pointeurs valent NULL. */
    

    Une fois que l'arbre a été construit, nous pouvons le manipuler, par exemple pour calculer la valeur de l'expression mathématique.

    Pourquoi s'embêter à construire l'arbre ? Ne pouvons-nous pas calculer la valeur au fur et à mesure de l'analyse ?


    C'est juste, mais en procédant ainsi, on perd en flexibilité. Et si notre but n'était pas simplement de calculer la valeur, mais de transcrire la formule dans une autre notation ? Ou encore, si la formule comporte des variables dont nous ne connaîtrons la valeur que plus tard, comment faire ? Nous pourrions aussi imaginer calculer la dérivée d'une fonction dont on connaîtrait la formule, etc.

    Comme vous le voyez, les possibilités sont nombreuses ! Nous nous servirons de cet analyseur dans d'autres exercices, plus tard. Il sera alors important de disposer de l'arbre, plutôt que de tout calculer directement. ;)

    Consignes



    Écrivez un programme qui :
    • 1. lit une expression mathématique sur l'entrée standard ;
    • 2. l'analyse pour en construire l'arbre ;
    • 3. calcule la valeur.

    L'expression mathématique tiendra sur une seule ligne et ne fera pas plus de 200 caractères, en comptant le '\n' et le '\0' terminaux.

    La nature des expressions, autrement dit le « langage » dans lequel les formules seront données, dépend du niveau de difficulté choisi.

    Objectifs


    • Chercher par soi-même sur Internet.
    • Découvrir ce qu'est un parser, comment ça marche.
    • Implémenter un algorithme connu.
    • Manipuler les chaînes de caractères.


    Énoncé



    Niveau 1

    Dans ce niveau, les formules vous sont données en notation polonaise inversée. C'est un nom technique, mais il n'y a rien de compliqué ; si vous savez parler comme maître Yoda, vous connaissez déjà. Au lieu d'avoir des opérations de la forme « 3+2 », elles sont de la forme « 3 2 + ». Ainsi,
    (3+2)*5/4

    s'écrit en notation polonaise inversée :
    3 2 + 5 * 4 /


    Les règles pour comprendre une formule écrite à l'aide de cette notation sont les suivantes. Prenez une pile, initialement vide, et lisez les symboles (nombre ou opérateur) un par un. Pour chaque symbole :
    • s'il s'agit d'un nombre, ajoutez-le au sommet de la pile ;
    • s'il s'agit d'un opérateur, dépilez deux nombres, appliquez-leur l'opération, empilez le résultat.

    Cette notation a pour caractéristique particulière qu'elle est facile à parser. Comme vous avez pu le constater sur l'exemple, il n'y a jamais besoin de parenthèses avec cette notation : les expressions sont non-ambiguës ! Nous n'avons pas non-plus besoin de priorité des opérateurs, puisque les opérations apparaissent dans l'ordre où elles sont effectuées.

    Voilà, vous avez toutes les clés en main pour réussir le niveau 1. Vous pouvez gérer les opérateurs que vous voulez, mais au moins +, -, *, et ^ (puissance).

    Bonne chance ! :)

    Bonus : toutes les formules données en notation polonaise inversée peuvent être écrites de façon à ce que tous les opérateurs soient à la fin. Par exemple,
    3 2 + 5 * 4 /

    peut s'écrire
    4 5 2 3 + * /

    Utilisez votre analyseur pour faire un programme qui récrit une formule en notation polonaise inversée de cette manière. Attention : le but de la manœuvre n'est pas de faire de la manipulation de texte, mais bien de construire l'arbre, puis d'afficher la formule sous une nouvelle forme à partir de cet arbre.

    Niveau 2

    Pour le niveau 2, nous allons parser une expression mathématique de « tous les jours ».

    Cette fois-ci, ce ne sera pas aussi simple, d'une part car il s'agit d'un langage plus compliqué, d'autre part car je vous guiderai moins. :)

    Je vais vous donner le langage que vous avez à parser sous la forme d'une grammaire formelle. Elle rentre dans la catégorie des grammaires algébriques, qui peuvent être reconnues grâce à un automate à pile.
    E -> (E)
    E -> E+E
    E -> E-E
    E -> E*E
    E -> E/E
    E -> N
    N -> n'importe quel entier positif.


    Cette grammaire est donnée par un ensemble de règles ; ici, E signifie « Expression » et N « nombre ». La première règle se lit comme suit : « une expression peut être formée d'un symbole '(', suivi d'une expression, suivi du symbole ')' ». La deuxième règle se lit : « une expression peut être formée d'une expression suivie du symbole '+', suivi d'une autre expression ». Et ainsi de suite...

    Une approche naïve est de tenter d'appliquer ces règles l'une après l'autre. Par exemple, pour la deuxième règle, on cherche un symbole « + » dans la chaîne, puis on analyse ce qui se trouve à gauche et à droite dudit symbole. Si on ne trouve aucun symbole « + », on essaie le symbole « - », puis « * », etc. Il est alors simple de construire l'arbre au fur et à mesure. La gestion des parenthèses et alors un poil délicate, mais l'idée reste la même. :)

    Une autre méthode consiste en utiliser un algorithme d'analyse connu. Un bon exemple est l'analyse LR, sur laquelle vous pouvez trouver de nombreuses informations sur le Web. Notez qu'il vous faudra peut-être modifier la grammaire pour l'adapter à l'algorithme que vous vous proposer d'utiliser. On peut en effet effectuer certaines transformations sur la grammaire sans pour autant en changer le sens. En outre, certains algorithmes demandent des grammaires écrites sous une forme bien précise. Je ne vous en dit pas plus, je vous laisse vous débrouiller. :)

    Le mot d'ordre pour le niveau 2 est : n'hésitez pas à chercher sur le Web. Je ne parle pas de copier/coller des codes tout faits, bien entendu, mais plutôt de chercher quelles techniques et quels algorithmes existent, puis de tenter de les implémenter. N'oubliez pas que le forum est là pour vous aider !

    Bon courage à tous ! ;)

    Bonus : plutôt que de calculer la valeur de l'expression mathématique, pourquoi ne pas la traduire en notation polonaise inversée ?

    Niveau 3

    Le but est maintenant de rajouter une nouvelle fonctionnalité à votre parser. Il doit comprendre les variables, dont le nom sera constitué d'une unique lettre en minsucule.

    Vous devez donc modifier votre programme du niveau 2 pour qu'il liste une expression mathématique comprenant des variables. Il devra ensuite demander la valeur de chacune des variables apparaissant dans l'expression, puis afficher le résultat du calcul.

    Conclusion



    Bon courage à tous ! :)
    GuilOooo


    S'il y a quelque chose que vous ne comprenez pas, n'hésitez pas à poser votre question sur ce sujet, nous vous répondrons avec plaisir ! :)


    Participants



    Participants Code
    Pouet_forever Niveau 3
    GurneyH Niveau 3
    Lucas-84 Niveau 1
    Tosh Niveau 3 - 1ère version
    Niveau 3 - 2ème version
    Niveau 3 - 3ème version
    GuilOooo Niveau 2
    remram44 Niveau 2 - 1ère version
    Niveau 2 - 2ème version
    Taurre Niveau 1
    neowillow Niveau 1 et niveau 2
    Shaddan Niveau 3
    informaticienzero Niveau 1
    • Partager sur Facebook
    • Partager sur Twitter
      30 septembre 2011 à 19:11:37

      L'énoncé est alléchant... ^^
      • Partager sur Facebook
      • Partager sur Twitter
      Staff désormais retraité.
        30 septembre 2011 à 19:20:42

        Et moi qui comence juste a faire un programme pareille :p .
        • Partager sur Facebook
        • Partager sur Twitter
          30 septembre 2011 à 19:52:53

          Comme le sujet a déjà été donné, je me permet de mettre mon code fait dans le précédent exo :

          #include <ctype.h>
          #include <errno.h>
          #include <math.h>
          #include <stdio.h>
          #include <stdlib.h>
          #include <string.h>
          
          
          #pragma mark MACRO
          /* **************** Macro **************** */
          
          #define CHECK_PUSH_STACK(stack, val, type)      \
            do {                                          \
              if (pushStack(stack, val, type) < 0)        \
                return -1;                                \
            } while (0)
          
          #define CHECK_POP_STACK(affect, stack)          \
            do {                                          \
              int type_MCPS;                              \
              affect = popStack(stack, &type_MCPS);       \
              if (type_MCPS == C_NOOP)                    \
                return -1;                                \
            } while (0)
          
          #pragma mark CONSTANT
          /* **************** Constant **************** */
          
          #define N_MAX     100
          #define SZ_NAME   50
          
          enum {
            E_OK,       /* No error                 */
            E_VAFF,     /* Affectation error        */
            E_BRAC,     /* Bracket error            */
            E_ARGS,     /* Number of arguments      */
            E_OPER,     /* Operator error           */
            E_OPAG,     /* Operator havn't 2 oprands*/
            E_VARN,     /* Variable name error      */
            E_LENV,     /* Variable name too long   */
            E_UERR,     /* Unexpected error         */
            E_FUNC,     /* Function call error      */
            E_VARE,     /* Variable does not exist  */
            E_STKF,     /* Stack full               */
            E_STKE,     /* Stack empty              */
            E_MALC,     /* Malloc error             */
            E_OPUK,     /* Unknown operator         */
            E_VARA,     /* Non modifiable variable  */
            E_DIV0,     /* Divide by 0              */
            E_OPCO,     /* Operator ',' not in func */
            E_NUMB,     /* 2 numbers                */
            N_ERRS      /* Number of errors         */
          };
          
          enum {
            C_NOOP,
            C_OPER,
            C_NUM,
            C_VAR,
            C_FUNC,
            C_AFFECT
          };
          
          #pragma mark STRUCTURE/UNION
          /* **************** Structure **************** */
          
          typedef struct stack_s {
            int st[N_MAX][2];           /* Stack : [0] -> Value, [1] -> type  */
            char name[N_MAX][SZ_NAME];  /* Variable name                      */
            int i;                      /* Index                              */
          } stack;
          
          typedef struct n_stack_s {
            stack st[2];
          } n_stack;
          
          typedef struct llist_val_s {
            struct llist_val_s * nxt;
            int val;                  /* Value  */
            char name[SZ_NAME];       /* Name   */
          } llist_val;
          
          typedef struct llist_s {
            llist_val * ans;
            llist_val * begin;
            llist_val * end;
            llist_val * add; /* Number of variable for affectation */
          } llist;
          
          typedef struct state_s {
            int affect;
            int current;
            int val_cur_op;
            int sig;
            int func;
          } estate;
          
          typedef struct funcs_s {
            char * name;
            int args;
          } funcs;
          
          typedef union {
            int val;
            char * name;
          } type_u;
          
          #pragma mark GLOBAL
          /* **************** Global **************** */
          
          llist list_g;
          int error;
          
          char * s_error[N_ERRS] = {
            "",
            "Affectation error",
            "Bracket error",
            "Invalid number of arguments",
            "Two operators which follow",
            "Operator must have 2 operands",
            "Incorrect variable name",
            "Variable name too long",
            "Unexpected error",
            "Function call",
            "Variable does not exist",
            "Stack full, expression too long",
            "Stack empty",
            "Malloc error",
            "Unknown operator",
            "Non modifiable variable",
            "Divide by 0",
            "Operator ',' must be in function call",
            "Two number which follow"
          };
          
          char * s_reservedName[] = {
            "q", "quit",
            "c", "clear",
            "h", "help",
            NULL
          };
          
          #define N_FUNC_1_ARG    9
          
          funcs func_call[] = {
            { "cos"   , 1 },
            { "sin"   , 1 },
            { "tan"   , 1 },
            { "fabs"  , 1 },
            { "exp"   , 1 },
            { "log"   , 1 },
            { "log10" , 1 },
            { "log2"  , 1 },
            { "sqrt"  , 1 },
            { "pow"   , 2 },
            { "hypot" , 2 },
            { NULL    , 0 }
          };
          
          double (*f1[])(double) = {
            cos, sin, tan, fabs, exp, log, log10, log2, sqrt
          };
          double (*f2[])(double, double) = {
            pow, hypot
          };
          
          #pragma mark PROTOTYPE
          /* **************** Prototype **************** */
          
          char * calcGetError(void);
          void calcSetError(int err);
          void printError(char const * s);
          
          llist_val * searchLlist(llist * l, char * name);
          llist * pushLlist(llist * l, llist_val * val);
          llist * popLlist(llist * l);
          
          int pushStack(stack * st, type_u var, int type);
          type_u popStack(stack * st, int * type);
          int popAllStack(n_stack * st);
          
          char * skipSpace(char * s);
          void delEnter(char * s);
          
          char * getVar(char * s, char * name);
          int checkVar(char * s, int * n);
          int applyVar(char ** src, estate * state, n_stack * st, char * var);
          int handleVar(char * src, char ** dst, estate * state, n_stack * st);
          int handleFunc(char * src, char ** dst, estate * state, n_stack * st, int n_f);
          
          int isOper(int c);
          int setPrio(int op);
          int priority(int op1, int op2);
          int handleOper(char * src, char ** dst, estate * state, n_stack * st);
          
          int convertNPI(char * src, char ** dst, n_stack * st, estate * state);
          int calcNPI(stack * st);
          int calcExp(char * s);
          void printHelp(void);
          int evaluate(char * s);
          int prompt(void);
          void release_llist(void);
          int init(void);
          
          #pragma mark FUNCTION
          /* **************** Function **************** */
          
          #pragma mark Errors
          /* >> Gestion d'erreur << */
          
          char * calcGetError(void) {
            return s_error[error];
          }
          
          void calcSetError(int err) {
            if (err < 0 || err >= N_ERRS)
              return;
            error = err;
          }
          
          void printError(char const * s) {
            if (s != NULL)
              fprintf(stderr, "%s ", s);
            fprintf(stderr, "%s\n", calcGetError());
          }
          
          #pragma mark Linked lists
          /* >> Gestion de la liste chaînée << */
          
          llist_val * searchLlist(llist * l, char * name) {
            llist_val * tmp;
            
            if (!l)
              return NULL;
            
            tmp = l->begin;
            while (tmp != NULL && strcmp(tmp->name, name)) {
              tmp = tmp->nxt;
            }
            if (tmp == NULL)
              calcSetError(E_VARE);
            
            return tmp;
          }
          
          llist * pushLlist(llist * l, llist_val * val) {
            llist_val * new;
            
            if (!l)
              return NULL;
            
            new = malloc(sizeof *new);
            if (!new) {
              calcSetError(E_MALC);
              return NULL;
            }
            
            new->nxt = NULL;
            new->val = val->val;
            strcpy(new->name, val->name);
            
            if (l->begin == NULL) {
              l->begin = new;
              l->end = new;
            }
            else {
              l->end->nxt = new;
              l->end = new;
            }
            return l;
          }
          
          llist * popLlist(llist * l) {
            llist_val * tmp;
            
            if (!l || !l->begin)
              return NULL;
            
            tmp = l->begin->nxt;
            free(l->begin);
            l->begin = tmp;
            return l;
          }
          
          #pragma mark Stacks
          /* >> Gestion de la pile << */
          
          int pushStack(stack * st, type_u var, int type) {
            
            if (((type == C_VAR || type == C_FUNC) && var.name == NULL) ||
                st == NULL)
              return -1;
            
            if (st->i >= N_MAX-1) {
              calcSetError(E_STKF);
              return -1;
            }
            
            if (type == C_VAR) {
              st->st[st->i][0] = 'V';
              strcpy(st->name[st->i], var.name);
            }
            else if (type == C_AFFECT) {
              st->st[st->i][0] = '=';
              strcpy(st->name[st->i], var.name);
            }
            else if (type == C_NUM || type == C_OPER || type == C_FUNC)
              st->st[st->i][0] = var.val;
            
            st->st[st->i][1] = type;
            st->i++;
            
            return 0;
          }
          
          type_u popStack(stack * st, int * type) {
            type_u tmp;
            int tmp_type;
            int i;
            
            if (st->i <= 0) {
              *type = C_NOOP;
              calcSetError(E_STKE);
              tmp.val = 0;
              return tmp;
            }
            
            i = st->i - 1;
            tmp_type = st->st[i][1];
            
            if (tmp_type == C_OPER || tmp_type == C_NUM || tmp_type == C_FUNC) {
              *type = tmp_type;
              tmp.val = st->st[i][0];
            }
            else if (tmp_type == C_VAR || tmp_type == C_AFFECT) {
              *type = tmp_type;
              tmp.name = st->name[i];
            }
            else {
              *type = C_NOOP;
              calcSetError(E_OPER);
              tmp.val = 0;
              return tmp;
            }
            
            st->st[i][0] = 0;
            st->st[i][1] = C_NOOP;
            st->i--;
            
            return tmp;
          }
          
          int popAllStack(n_stack * st) {
            type_u v;
            int type;
            
            while (1) {
              v = popStack(&st->st[1], &type);
              if (type == C_NOOP)
                break;
              if (pushStack(&st->st[0], v, type) < 0)
                return -1;
            }
            /* La pile 2 est vide : error est set à E_NOOP */
            calcSetError(E_OK);
            return 0;
          }
          
          #pragma mark Strings
          /* >> Fonctions sur les chaînes de caractères << */
          
          char * skipSpace(char * s) {
            while (isspace(*s))
              s++;
            return s;
          }
          
          void delEnter(char * s) {
            char * c = strchr(s, '\n');
            
            if (c)
              *c = '\0';
          }
          
          #pragma mark Variables & functions
          /* >> Gestion des variables et fonctions << */
          
          char * getVar(char * s, char * name) {
            int i;
            
            i = 0;
            while (isalnum(*s) && i < SZ_NAME) {
              name[i] = *s;
              s++;
              i++;
            }
            
            if (i >= SZ_NAME) {
              calcSetError(E_LENV);
              return NULL;
            }
            
            name[i] = '\0';
            return s;
          }
          
          int checkVar(char * s, int * n) {
            char ** tmp;
            int i;
            
            for (i = 0; func_call[i].name != NULL; i++) {
              if (strcmp(func_call[i].name, s) == 0) {
                *n = i;
                return C_FUNC;
              }
            }
            
            tmp = s_reservedName;
            while (*tmp != NULL) {
              if (strcmp(*tmp, s) == 0) {
                calcSetError(E_VARN);
                return C_NOOP;
              }
              tmp++;
            }
            
            return C_VAR;
          }
          
          int applyVar(char ** src, estate * state, n_stack * st, char * var) {
            type_u v;
            
            /* Affectation */
            if (*(*src) == '=' && state->affect) {
              if (strcmp(var, "ans") == 0) {
                calcSetError(E_VARA);
                return -1;
              }
              (*src)++;
              v.name = var;
              CHECK_PUSH_STACK(&st->st[1], v, C_AFFECT);
              state->current = C_AFFECT;
            }
            /* Affectation error */
            else if (*(*src) == '=' && !state->affect) {
              calcSetError(E_VAFF);
              return -1;
            }
            /* Variable */
            else {
              llist_val * var_l;
              
              if (strcmp(var, "ans") == 0)
                var_l = list_g.ans;
              else
                var_l = searchLlist(&list_g, var);
              
              if (var_l == NULL) {
                return -1;
              }
              /* Variable exists */
              else {
                if (state->current == C_NOOP || state->current == C_OPER ||
                    state->current == C_AFFECT) {
                  v.val = var_l->val;
                  CHECK_PUSH_STACK(&st->st[0], v, C_NUM);
                  state->current = C_NUM;
                  state->affect = 0;
                }
                else {
                  calcSetError(E_OPER);
                  return -1;
                }
              }
            }
            return 0;
          }
          
          int handleVar(char * src, char ** dst, estate * state, n_stack * st) {
            char var[SZ_NAME];
            int chk, n_f;
            
            src = getVar(src, var);
            if (src == NULL)
              return -1;
            
            chk = checkVar(var, &n_f);
            if (chk == C_NOOP)
              return -1;
            
            src = skipSpace(src);
            
            /* Variable */
            if (chk == C_VAR) {
              if (applyVar(&src, state, st, var) < 0)
                return -1;
            }
            /* Function */
            else if (chk == C_FUNC) {
              if (*src == '(') {
                if (handleFunc(src, &src, state, st, n_f) < 0)
                  return -1;
              }
              /* Function error */
              else {
                calcSetError(E_FUNC);
                return -1;
              }
            }
            /* Should never happen */
            else {
              return -1;
            }
            
            if (dst != NULL)
              *dst = src;
            return 0;
          }
          
          int handleFunc(char * src, char ** dst, estate * state, n_stack * st, int n_f) {
            int i, tmp, type, nb_args;
            type_u v;
            
            state->func = 1;
            state->current = C_OPER;
            state->val_cur_op = *src;
            src++;
            
            nb_args = func_call[n_f].args;
            for (i = 0; i < nb_args; i++) {
              v.val = '(';
              CHECK_PUSH_STACK(&st->st[1], v, C_OPER);
              
              state->affect = 1;
              state->current = C_OPER;
              state->val_cur_op = '(';
              
              convertNPI(src, &src, st, state);
              if (error != E_OK)
                return -1;
              
              src = skipSpace(src);
              if ((i < nb_args - 1 && *src != ',') && (i == nb_args - 1 && *src != ')')) {
                calcSetError(E_BRAC);
                return -1;
              }
              src++;
              
              tmp = st->st[1].i;
              while (st->st[1].st[tmp-1][0] != '(') {
                v = popStack(&st->st[1], &type);
                if (type == C_NOOP)
                  return -1;
                CHECK_PUSH_STACK(&st->st[0], v, type);
                tmp = st->st[1].i;
              }
              /* Bracket */
              popStack(&st->st[1], &type);
            }
            
            v.val = n_f;
            CHECK_PUSH_STACK(&st->st[0], v, C_FUNC);
            
            state->func = 0;
            if (dst != NULL)
              *dst = src;
            return 0;
          }
          
          #pragma mark Operators
          /* >> Gestion des opérateurs << */
          
          int isOper(int c) {
            char oper[] = { '+', '-', '*', '/', '%', '(', ')', '^' };
            int sz = sizeof oper / sizeof *oper;
            int i;
            
            for (i = 0; i < sz; i++)
              if (c == oper[i])
                return 1;
            return 0;
          }
          
          int setPrio(int op) {
            int i, sz;
            struct {
              char op;
              int val;
            } equ[] = {
              { '(', 0 },
              { '=', 1 },
              { '+', 2 },
              { '-', 2 },
              { '*', 3 },
              { '/', 3 },
              { '%', 3 },
              { '^', 4 }
            };
            
            sz = sizeof equ / sizeof *equ;
            for (i = 0; i < sz; i++)
              if (op == equ[i].op)
                return equ[i].val;
            
            /* Unknown operator */
            return -1;
          }
          
          /* '=' < '(' < '+' < '-' < '*' < '/' < '^' */
          int priority(int op1, int op2) {
            if (op1 == '^' && op2 == '^')
              return 1;
            op1 = setPrio(op1);
            op2 = setPrio(op2);
            return op1 > op2;
          }
          
          int handleOper(char * src, char ** dst, estate * state, n_stack * st) {
            type_u v;
            int type;
            int tmp;
            
            if (*src == '(') {
              if ((state->current == C_OPER && state->val_cur_op == ')') ||
                  state->current == C_NUM) {
                v.val = '*';
                CHECK_PUSH_STACK(&st->st[1], v, C_OPER);
              }
              
              v.val = *src;
              CHECK_PUSH_STACK(&st->st[1], v, C_OPER);
              
              state->affect = 1;
              state->current = C_OPER;
              state->val_cur_op = '(';
              src++;
              
              convertNPI(src, &src, st, state);
              if (error != E_OK)
                return -1;
              
              src = skipSpace(src);
              if (*src != ')') {
                calcSetError(E_BRAC);
                return -1;
              }
              src++;
              
              /* Si c'est un parenthèse fermante on dépop toute la stack 2
               * jusqu'à la parenthèse */
              tmp = st->st[1].i;
              while (st->st[1].st[tmp-1][0] != '(') {
                v = popStack(&st->st[1], &type);
                if (type == C_NOOP)
                  return -1;
                CHECK_PUSH_STACK(&st->st[0], v, type);
                tmp = st->st[1].i;
              }
              /* Bracket */
              popStack(&st->st[1], &type);
            }
            else {
              if (state->current == C_OPER && state->val_cur_op != '(' &&
                  state->val_cur_op != ')') {
                calcSetError(E_OPER);
                return -1;
              }
              
              /* On ajoute sur la stack 2 si elle est vide, sinon on regarde
               * la priorité des opérateurs.
               * Si l'opérateur en cours a une prio plus importante, on le stocke,
               * sinon on depop sur la stack 1 */
              tmp = st->st[1].i;
              if (st->st[1].i == 0 || priority(*src, st->st[1].st[tmp-1][0]) == 1) {
                v.val = *src;
                CHECK_PUSH_STACK(&st->st[1], v, C_OPER);
              }
              else {
                /* Tant que la prio de l'op en cours est inférieure à celle
                 * sur la stack, on dépile sur la stack 1 */
                while (priority(*src, st->st[1].st[tmp-1][0]) != 1) {
                  v = popStack(&st->st[1], &type);
                  if (type == C_NOOP)
                    return -1;
                  CHECK_PUSH_STACK(&st->st[0], v, type);
                  tmp = st->st[1].i;
                }
                /* On mets l'op sur la stack 2 */
                v.val = *src;
                CHECK_PUSH_STACK(&st->st[1], v, C_OPER);
              }
              
              state->current = C_OPER;
              state->val_cur_op = *src;
              src++;
            }
            
            if (dst != NULL)
              *dst = src;
            return 0;
          }
          
          #pragma mark Calc
          /* >> Fonctions de gestion et de calcul << */
          
          int convertNPI(char * src, char ** dst, n_stack * st, estate * state) {
            type_u v;
            
            state->sig = 1;
            
            while (*src) {
              src = skipSpace(src);
              
              /* Variable */
              if (isalpha(*src)) {
                if (handleVar(src, &src, state, st) < 0)
                  return -1;
              }
              else if (*src == '-') {
                src++;
                src = skipSpace(src);
                
                if (state->current == C_NOOP || state->current == C_AFFECT ||
                    (state->current == C_OPER && state->val_cur_op == '(')) {
                  v.val = '*';
                  CHECK_PUSH_STACK(&st->st[1], v, C_OPER);
                  v.val = -1;
                  CHECK_PUSH_STACK(&st->st[0], v, C_NUM);
                  
                  state->sig = -1;
                }
                else if ((state->current == C_OPER && state->val_cur_op == ')') ||
                         state->current == C_FUNC || state->current == C_NUM) {
                  char tmp_s[2] = "-";
                  
                  if (handleOper(tmp_s, NULL, state, st) < 0)
                    return -1;
                }
                else {
                  calcSetError(E_OPER);
                  return -1;
                }
              }
              else if (isdigit(*src)) {
                int n;
                
                if (state->current == C_NUM) {
                  calcSetError(E_NUMB);
                  return -1;
                }
                else if (state->current == C_OPER && state->val_cur_op == ')') {
                  v.val = '*';
                  CHECK_PUSH_STACK(&st->st[1], v, C_OPER);
                }
                
                errno = 0;
                n = (int) strtol(src, &src, 10);
                if (errno) {
                  perror("");
                  return -1;
                }
                
                v.val = n;
                CHECK_PUSH_STACK(&st->st[0], v, C_NUM);
                state->current = C_NUM;
              }
              else if (isOper(*src)) {
                if (*src == ')') {
                  state->current = C_OPER;
                  state->val_cur_op = *src;
                  break;
                }
                if (handleOper(src, &src, state, st) < 0)
                  return -1;
              }
              else if (*src == ',') {
                if (state->func) {
                  state->current = C_OPER;
                  state->val_cur_op = *src;
                  break;
                }
                else {
                  calcSetError(E_OPCO);
                  return -1;
                }
              }
              else {
                calcSetError(E_UERR);
                return -1;
              }
            }
            
            *dst = src;
            return 0;
          }
          
          int calcNPI(stack * st) {
            type_u v;
            int n1, n2, op;
            int i;
            
            i = st->i;
            
            if (error)
              return -1;
            else if (i == 0) {
              calcSetError(E_OPAG);
              return -1;
            }
            else if (st->st[i-1][1] == C_NUM) {
              CHECK_POP_STACK(v, st);
              return v.val;
            }
            else if (st->st[i-1][1] == C_AFFECT) {
              llist_val lv;
              
              strcpy(lv.name, st->name[i-1]);
              CHECK_POP_STACK(v, st);
              n1 = calcNPI(st);
              if (error)
                return -1;
              
              lv.val = n1;
              if (pushLlist(&list_g, &lv) == NULL)
                return -1;
              if (list_g.add == NULL)
                list_g.add = list_g.end;
              return n1;
            }
            else if (st->st[i-1][1] == C_FUNC) {
              int tmp;
              funcs * f;
              
              tmp = st->st[i-1][0];
              f = &func_call[tmp];
              CHECK_POP_STACK(v, st);
              if (f->args == 1) {
                n1 = calcNPI(st);
                return f1[tmp](n1);;
              }
              else if (f->args == 2) {
                n1 = calcNPI(st);
                n2 = calcNPI(st);
                return f2[tmp-N_FUNC_1_ARG](n2, n1);
              }
            }
            else if (st->st[i-1][1] == C_OPER) {
              op = st->st[i-1][0];
              CHECK_POP_STACK(v, st);
              n1 = calcNPI(st);
              n2 = calcNPI(st);
              
              if (op == '+')
                return n2 + n1;
              else if (op == '-')
                return n2 - n1;
              else if (op == '*')
                return n2 * n1;
              else if (op == '%') {
                if (n1 == 0) {
                  calcSetError(E_DIV0);
                  return -1;
                }
                return n2 % n1;
              }
              else if (op == '/') {
                if (n1 == 0) {
                  calcSetError(E_DIV0);
                  return -1;
                }
                return n2 / n1;
              }
              else if (op == '^')
                return pow(n2, n1);
              else {
                /* Should never happen... */
                calcSetError(E_OPUK);
                return -1;
              }
            }
            else {
              calcSetError(E_UERR);
              return -1;
            }
            
            return -1;
          }
          
          int calcExp(char * s) {
            int n;
            n_stack st;
            estate state;
            
            state.affect = 1;
            state.current = 0;
            state.sig = 1;
            state.val_cur_op = 0;
            state.func = 0;
            
            s = skipSpace(s);
            if (*s == '\0')
              return 0;
            
            memset(&st, 0, sizeof st);
            
            if (convertNPI(s, &s, &st, &state) < 0) {
              return -1;
            }
            if (!error && *s != '\0') {
              calcSetError(E_UERR);
              return -1;
            }
            if (popAllStack(&st) < 0)
              return -1;
            
            n = calcNPI(&st.st[0]);
            if (error)
              return -1;
            
            printf("-: %d\n", n);
            while (list_g.add != NULL) {
              printf("%s = %d\n", list_g.add->name, list_g.add->val);
              list_g.add = list_g.add->nxt;
            }
            list_g.ans->val = n;
            
            return 0;
          }
          
          void printHelp(void) {
            printf("q, quit   :> Quitter\n");
            printf("c, clear  :> Supprimer les variables\n");
            printf("h, help   :> Affiche l'aide\n");
          }
          
          int evaluate(char * s) {
            int ret;
            
            delEnter(s);
            s = skipSpace(s);
            
            if (strcmp(s, "quit") == 0 || strcmp(s, "q") == 0)
              ret = 1;
            else if (strcmp(s, "clear") == 0 || strcmp(s, "c") == 0) {
              list_g.ans->val = 0;
              while (popLlist(&list_g) != NULL)
                ;
              ret = 0;
            }
            else if (strcmp(s, "help") == 0 || strcmp(s, "h") == 0) {
              printHelp();
            }
            else if (strcmp(s, "ans") == 0) {
              printf("ans = %d\n", list_g.ans->val);
              ret = 0;
            }
            else {
              ret = calcExp(s);
            }
            
            return ret;
          }
          
          int prompt(void) {
            int tmp;
            int done = 0;
            char s[N_MAX] = "";
            
            while (!done) {
              printf(">>> ");
              if (fgets(s, N_MAX, stdin) == NULL) {
                perror("Erreur fgets");
                continue;
              }
              tmp = evaluate(s);
              
              if (tmp < 0 && error != E_OK) {
                printError(NULL);
                calcSetError(E_OK);
              }
              else if (tmp > 0)
                done = 1;
            }
            
            return 0;
          }
          
          void release_llist(void) {
            free(list_g.ans);
            while (list_g.begin != NULL)
              popLlist(&list_g);
          }
          
          int init(void) {
            list_g.ans = malloc(sizeof *list_g.ans);
            
            if (list_g.ans == NULL) {
              calcSetError(E_MALC);
              return -1;
            }
            
            list_g.ans->nxt = NULL;
            list_g.ans->val = 0;
            strcpy(list_g.ans->name, "ans");
            
            list_g.begin = NULL;
            list_g.end = NULL;
            list_g.add = NULL;
            
            atexit(release_llist);
            
            calcSetError(E_OK);
            
            return 0;
          }
          
          int main(void) {
            if (init() < 0) {
              printError(NULL);
              return EXIT_FAILURE;
            }
            return prompt() ? EXIT_FAILURE : EXIT_SUCCESS;
          }
          

          Exemple d'exécution :

          >>> (1+2)-(3*4)(5*6)
          -: -357
          >>> -1*4+2
          -: -2
          >>> pow(5,3)
          -: 125
          >>> 5^2^2
          -: 625
          >>> 5^2^2%4
          -: 1
          >>> q

          Enjoy !
          • Partager sur Facebook
          • Partager sur Twitter
            30 septembre 2011 à 20:03:02

            @Pouet_forever : je cherchais la fin du code mais ce n'est qu'a la 1023 eme ligne que je l'ai trouvé :-° .
            Mais c'est logique vue le resultat.

            Ça promet en tout cas !
            • Partager sur Facebook
            • Partager sur Twitter
              30 septembre 2011 à 21:37:17

              Citation

              Le défi #1 est un jeu d'enfant à côté !

              C'est sûr que ça motive ça. :-°

              Je vais faire de mon mieux pour écrire un code dans les jours à venir.

              Merci encore de vous donner du mal. ;)
              • Partager sur Facebook
              • Partager sur Twitter
                30 septembre 2011 à 22:30:06

                Han!
                La dernière fois (pour le truc de chimie) j'avais proposé un truc, on m'avait répondu "non faire un parser complet était bien trop compliqué etc." là vous proposez quoi ? ^^

                Au final les gens assidus qui auront fait les deux exos comprendront leur temps perdu sur le premier (cf. parser non-générique).
                • Partager sur Facebook
                • Partager sur Twitter
                Anonyme
                  30 septembre 2011 à 22:46:20

                  Sympa, je le ferai peut-être celui-là. :)
                  • Partager sur Facebook
                  • Partager sur Twitter
                  Anonyme
                    1 octobre 2011 à 0:50:59

                    Intéressant, ça me fera laisser les outils comme flex et bison pour me lancer dans l'interprétation made at home :D
                    • Partager sur Facebook
                    • Partager sur Twitter
                      1 octobre 2011 à 1:44:36

                      Encore un?!? ^^ Pour le trois, patiente le temps que j'apprenne tous ça!! (et que je trouve du temps libre pour le faire...).
                      De toutes façons, j'ai l'intention de réaliser chaque défi :diable: (vous avez plusieurs années devant vous j'espère :D )Merci encore aux concepteurs (surtout cepteurs) de ces défis!
                      • Partager sur Facebook
                      • Partager sur Twitter
                        1 octobre 2011 à 3:53:58

                        Bonjour

                        Comme Pouet_forever l'exo a déjà été réalisé ici.(et sur l'atelier calculatrice).

                        Il gère les variables, les flottants, les fonctions mathématiques style sin, cos, tan... Le tout en essayant de gérer les entrées erronées.
                        • Partager sur Facebook
                        • Partager sur Twitter
                        Zeste de Savoir, le site qui en a dans le citron !
                          1 octobre 2011 à 8:55:24

                          1023 lignes...
                          En effet, les quinze jours ne seront pas de trop. ^^

                          • Partager sur Facebook
                          • Partager sur Twitter
                          Staff désormais retraité.
                            1 octobre 2011 à 9:41:02

                            Tout comme @Pouet et @GurneyH, j'avais aussi participé au zCalc : http://www.siteduzero.com/forum-83-556 [...] html#r5365638 .

                            Mais je compte refaire l'exercice en utilisant cette fois un arbre binaire :) .
                            • Partager sur Facebook
                            • Partager sur Twitter
                              1 octobre 2011 à 11:06:23

                              Pour le niveau 1, j'ai fait ça :

                              /* main.c */
                              
                              #include <stdio.h>
                              #include <stdlib.h>
                              #include <ctype.h>
                              
                              enum { RET_FAILURE, RET_SUCCESS };
                              
                              typedef struct stack_s {
                                  double data;
                                  struct stack_s *p_next;
                              } stack_s;
                              
                              typedef unsigned int ret_t;
                              
                              /* Retourne 1 si c'est une opération, 0 sinon */
                              int isoperation(int c)
                              {
                                  if (c == '+' || c == '-' || c == '*' || c == '/' || c == '^') {
                              	return 1;
                                  }
                              
                                  return 0;
                              }
                              
                              /* Ajoute un élément */
                              ret_t stack_push(stack_s ** p_head, double value)
                              {
                                  stack_s *p_node = malloc(sizeof(stack_s));
                              
                                  if (!p_node) {
                              	perror("malloc");
                              	return RET_FAILURE;
                                  }
                              
                                  p_node->data = value;
                                  p_node->p_next = *p_head;
                                  *p_head = p_node;
                              
                                  return RET_SUCCESS;
                              }
                              
                              /* Enlève un élément */
                              ret_t stack_pop(stack_s ** p_head, double *value)
                              {
                                  if (!*p_head) {
                              	fprintf(stderr, "Stack empty\n");
                              	return RET_FAILURE;
                                  } else {
                              	stack_s *p_top = *p_head;
                              	if (value != NULL) {
                              	    *value = p_top->data;
                              	}
                              	*p_head = p_top->p_next;
                              	free(p_top);
                              	return RET_SUCCESS;
                                  }
                              }
                              
                              /* Détruit la pile */
                              ret_t stack_clear(stack_s ** p_head)
                              {
                                  while (*p_head != NULL) {
                              	stack_pop(p_head, NULL);
                                  }
                              
                                  return RET_SUCCESS;
                              }
                              
                              /* Calcule une puissance */
                              double pow(double nb, double n) 
                              {
                                  int i;
                                  int dep = nb;
                              
                                  for (i = 1 ; i < n ; i++) {
                              	nb *= dep;
                                  }
                              
                                  return nb;
                              }
                              
                              /* Fonction de calcul */
                              double calc(double n1, double n2, char oper)
                              {
                                  switch (oper) {
                                  case '+':
                              	return n1 + n2;
                              	break;
                                  case '-':
                              	return n2 - n1;
                              	break;
                                  case '*':
                              	return n1 * n2;
                              	break;
                                  case '/':
                              	return n2 / n1;
                              	break;
                                  case '^':
                              	return pow(n2, n1);
                              	break;
                                  default:
                              	return RET_FAILURE;
                              	break;
                                  }
                              }
                              
                              /* Analyse l'opération */
                              ret_t analyse(const char *s)
                              {
                                  stack_s *stack = NULL;
                                  double n1, n2, res;
                              
                                  do {
                              	if (isdigit(*s)) {
                              	    if (!stack_push(&stack, strtod(s, NULL))) {
                              		return RET_FAILURE;
                              	    }
                              	    while (*s != ' ' && *s++ != '\n');
                              	} else if (isoperation(*s)) {
                              	    if (!stack_pop(&stack, &n1)) {
                              		return RET_FAILURE;
                              	    }
                              	    if (!stack_pop(&stack, &n2)) {
                              		return RET_FAILURE;
                              	    }
                              	    res = calc(n1, n2, *s);
                              	    if (!stack_push(&stack, res)) {
                              		return RET_FAILURE;
                              	    }
                              	}
                                  } while (*s++);
                                  printf("Résultat = %f\n", stack->data);
                                  return RET_SUCCESS;
                              }
                              
                              int main(void)
                              {
                                  char s[200];
                                  fgets(s, sizeof(s), stdin);
                                  if(!analyse(s)) {
                              	return EXIT_FAILURE;
                                  }
                                  return EXIT_SUCCESS;
                              }
                              


                              Mais bon, je ne connaissais même pas la notation polonaise inversée, je suis donc ouvert à toutes les critiques... (vous avez dit inculte... :-° )

                              EDIT 1 : EXIT_SUCCESS au lieu de RET_SUCCESS
                              EDIT 2 : support de nombres supérieurs à 10
                              EDIT 3 : ajout d'une fonctionnalité de puissance
                              • Partager sur Facebook
                              • Partager sur Twitter
                              Staff désormais retraité.
                              Anonyme
                                1 octobre 2011 à 11:27:21

                                Hum, soit c'est moi qui ne sait pas écrire une expression en npi, soit ton code est un peu bugué :

                                [thibault@neow-desktop parser]$ gcc x.c -Wall -o x
                                [thibault@neow-desktop parser]$ ./x
                                30 10 +
                                Résultat = 10.000000
                                [thibault@neow-desktop parser]$ ./x
                                10 8 -
                                Résultat = -8.000000
                                [thibault@neow-desktop parser]$ ./x
                                10 2 *
                                Résultat = 0.000000
                                [thibault@neow-desktop parser]$ ./x 
                                60 2 /
                                Résultat = 0.000000

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  1 octobre 2011 à 11:35:21

                                  En effet, je viens de comprendre que mon code ne gérait pas les nombres plus strictement supérieurs à 9. Un peu gênant ! :lol:

                                  EDIT : c'est bon, a priori le bug est corrigé.
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                  Staff désormais retraité.
                                    1 octobre 2011 à 16:32:55

                                    Voilà ma contribution (pas encore achevée) pour ce défi :

                                    #include <string.h>
                                    #include <stdio.h>
                                    #include <stdlib.h>
                                    #include <assert.h>
                                    #include <ctype.h>
                                    #include <math.h>
                                    
                                    enum type
                                    {
                                       UNDEF,
                                       OPERATOR,
                                       VALUE,
                                       BRACE_LEFT,
                                       BRACE_RIGHT
                                    };
                                    
                                    enum operator
                                    {
                                       NONE,
                                       MUL,
                                       MOD,
                                       DIV,
                                       ADD,
                                       SUB,
                                       POW,
                                     };
                                    
                                    union u_type
                                    {
                                       enum operator op;
                                       double val;
                                    };
                                    
                                    struct token
                                    {
                                       enum type type;
                                       union u_type u_type;
                                    };
                                    
                                    struct tree
                                    {
                                       struct token token;
                                    
                                       struct tree *left;
                                       struct tree *right;
                                    };
                                    
                                    struct tree* create_node(struct tree *left, struct token *token, struct tree *right)
                                    {
                                       struct tree *node;
                                    
                                       if((node = malloc(sizeof(struct tree))) == NULL)
                                       {
                                          perror("malloc ");
                                          exit(EXIT_FAILURE);
                                       }
                                       
                                       node->token = *token;
                                       node->left = left;
                                       node->right = right;
                                    
                                       return node;
                                    }
                                    
                                    double calc_tree(struct tree *tree);
                                    
                                    double calc_node(struct tree *tree)
                                    {
                                       assert(tree->token.u_type.op != NONE);
                                    
                                       switch(tree->token.u_type.op)
                                       {
                                       case MUL:
                                          return calc_tree(tree->left) * calc_tree(tree->right);
                                       case DIV:
                                          return calc_tree(tree->left) / calc_tree(tree->right);
                                       case ADD:
                                          return calc_tree(tree->left) + calc_tree(tree->right);
                                       case SUB:
                                          return calc_tree(tree->left) - calc_tree(tree->right);
                                       case MOD:
                                          return fmod(calc_tree(tree->left), calc_tree(tree->right));
                                       case POW:
                                          return pow(calc_tree(tree->left), calc_tree(tree->right));
                                       case NONE:
                                          return 0.0;
                                       }
                                       return 0.0; 
                                    }
                                    
                                    /*
                                      /!\ RECURSIVE /!\
                                    */
                                    double calc_tree(struct tree *tree)
                                    {
                                       assert(tree != NULL);
                                       assert(tree->token.type == OPERATOR || tree->token.type == VALUE);
                                    
                                       switch(tree->token.type)
                                       {
                                       case OPERATOR:
                                          return calc_node(tree);
                                       case VALUE:
                                          return tree->token.u_type.val;
                                       default:
                                          return 0.0;
                                       }
                                       return 0.0;
                                    }
                                    
                                    int priority(enum operator op)
                                    {
                                       switch(op)
                                       {
                                       case SUB:
                                       case ADD:
                                          return 1;
                                       case MUL:
                                       case DIV:
                                       case MOD:
                                          return 2;
                                       case POW:
                                          return 3;
                                       case NONE:
                                          return 0;
                                       }
                                       return 0;
                                    }
                                    
                                    enum operator char_to_operator(int c)
                                    {
                                       if(c == '+')
                                          return ADD;
                                       else if(c == '-')
                                          return SUB;
                                       else if(c == '*')
                                          return MUL;
                                       else if(c == '/')
                                          return DIV;
                                       else if(c == '%')
                                          return MOD;
                                       else if(c == '^')
                                          return POW;
                                       
                                       return NONE;
                                    }
                                    
                                    int next_token( char **expr, struct token *token)
                                    {
                                       if(**expr == '\0')
                                          return 0;
                                    
                                       if(isdigit(**expr))
                                       {
                                          token->type = VALUE;
                                          token->u_type.val = strtod(*expr, expr);
                                       }
                                       else if(**expr == '(')
                                       {
                                          token->type = BRACE_LEFT;
                                          (*expr)++;
                                       }
                                       else if(**expr == ')')
                                       {
                                          token->type = BRACE_RIGHT;
                                          (*expr)++;
                                       }
                                       else
                                       {
                                          token->type = OPERATOR;
                                          token->u_type.op = char_to_operator(**expr);
                                          (*expr)++;
                                       }
                                      
                                       return 1;
                                    }
                                    
                                    /* 
                                       Check if the first and the last brace is usefull,
                                       if not, the function delete them.
                                       /!\ RECURSIVE /!\
                                     */
                                    void clean_useless_brace(char **string)
                                    {
                                       int level;
                                       char *ptr;
                                       int min_level;
                                    
                                       ptr = *string;
                                    
                                       if(*ptr == '\0' || *ptr != '(')
                                          return;
                                    
                                       min_level = 1;
                                       level = 0;
                                    
                                       while(*ptr != '\0')
                                       {
                                          if(*ptr == '(')
                                    	 level++;
                                          else if(*ptr == ')')
                                    	 level--;
                                    
                                          if(min_level > level && *(ptr+1) != '\0')
                                    	 min_level = level;
                                    
                                          ptr++;
                                       }
                                       if(min_level == 1)
                                       {
                                          **string = '\0';
                                          *(ptr-1) = '\0';
                                          (*string)++;
                                          clean_useless_brace(string);
                                       }
                                    }
                                    
                                    /*
                                      Divide a string into two string and a token. Check the operators's priority.
                                      Ex : (5+4)*2 
                                      Part left  : (5+4)
                                      Part right : 2
                                      Token      : *
                                     */
                                    void divide_string(char *string, char **left, struct token *token, char **right)
                                    {
                                       int level = 0;
                                       struct token cur_token;
                                       
                                       clean_useless_brace(&string);
                                       token->type = UNDEF;
                                    
                                       *left = string;
                                       *right = string;
                                    
                                    #ifdef DEBUG
                                       printf("****************\n");
                                       printf("==>'%s'\n", string);
                                    #endif
                                    
                                       while(next_token(&string, &cur_token))
                                       {
                                          if(cur_token.type == OPERATOR)
                                          {
                                    	 if(level == 0)
                                    	 {
                                    	    if(token->type == UNDEF || (priority(cur_token.u_type.op) <= priority(token->u_type.op)))
                                    	    {
                                    	       token->type = OPERATOR;
                                    	       token->u_type.op = cur_token.u_type.op;
                                    	       *right = string;
                                    	    }
                                    	 }
                                          }
                                          else if(cur_token.type == BRACE_LEFT)
                                          {
                                    	 level++;
                                          }
                                          else if(cur_token.type == BRACE_RIGHT)
                                          {
                                    	 level--;
                                          }
                                    
                                          if(token->type == UNDEF)
                                          {
                                    	 *right = string;
                                          }
                                       }
                                       if(token->type == UNDEF)
                                       {
                                          **left = **right = '\0';
                                          token->type = cur_token.type;
                                          token->u_type = cur_token.u_type;
                                       }
                                       else
                                       {
                                          *(*right-1) = '\0';
                                       }
                                    
                                    #ifdef DEBUG
                                       printf("RIGHT : '%s'\n", *right);
                                       printf("LEFT : '%s'\n", *left);
                                    #endif
                                    }
                                    
                                    /*
                                      /!\ RECURSIVE /!\
                                    */
                                    struct tree* build_tree(char *expr)
                                    {
                                       struct token token;
                                       char *right, *left;
                                    
                                       if(*expr == '\0')
                                          return NULL;
                                    
                                       divide_string(expr, &left, &token, &right);
                                       
                                       if(token.type == VALUE)
                                          return create_node(NULL, &token, NULL);
                                    
                                       return create_node(build_tree(left), &token, build_tree(right));
                                    }
                                    
                                    /*
                                      /!\ RECURSIVE /!\
                                    */
                                    void free_tree(struct tree *tree)
                                    {
                                       if(tree != NULL)
                                       {
                                          free_tree(tree->left);
                                          free_tree(tree->right);
                                          free(tree);
                                       }
                                    }
                                    
                                    #ifdef DEBUG
                                    char *operator_to_string(enum operator op)
                                    {
                                       assert(op != UNDEF);
                                    
                                       switch(op)
                                       {
                                       case MUL:
                                          return "*";
                                       case DIV:
                                          return "/";
                                       case ADD:
                                          return "+";
                                       case SUB:
                                          return "-";
                                       case MOD:
                                          return "%";
                                       case POW:
                                          return "^";
                                       case UNDEF:
                                          return "UNDEF";
                                       }
                                       return "";
                                    }
                                    
                                    void print_token(struct token *token)
                                    {
                                       switch(token->type)
                                       {
                                       case OPERATOR:
                                          printf("%s", operator_to_string(token->u_type.op));
                                          break;
                                       case VALUE:
                                          printf("%g", token->u_type.val);
                                          break;
                                       case BRACE_LEFT:
                                          printf("(");
                                          break;
                                       case BRACE_RIGHT:
                                          printf(")");
                                          break;
                                       case UNDEF:
                                          printf("UNDEF");
                                          break;
                                       }
                                    }
                                    
                                    /*
                                      /!\ RECURSIVE /!\
                                    */
                                    void print_tree(struct tree *tree, int prof)
                                    {
                                       int i;
                                    
                                       for(i = 0; i < prof; i++)
                                       {
                                          printf(" -- ");
                                       }
                                    
                                       if(tree)
                                       {
                                          print_token(&(tree->token));
                                          puts("");
                                          print_tree(tree->left, prof+1);
                                          print_tree(tree->right, prof+1);
                                       }
                                       else
                                       {
                                          printf("||\n");
                                       }
                                    }
                                    #endif
                                    
                                    int main(int argc, char **argv)
                                    {
                                       struct tree *tree;
                                    
                                       if(argc != 2)
                                       {
                                          printf("Usage : %s <expr>\n", argv[0]);
                                          return EXIT_FAILURE;
                                       }
                                    
                                       tree = build_tree(argv[1]);
                                    
                                    #ifdef DEBUG
                                       print_tree(tree, 0);
                                    #endif
                                    
                                       printf("RESULT : %.2f\n", calc_tree(tree));
                                    
                                       free_tree(tree);
                                    
                                       return EXIT_SUCCESS;
                                    }
                                    


                                    J'utilise donc un arbre binaire pour le calcul de l'expression. L'algorithme est assez inefficace je pense, je vais le détailler un peu :

                                    Pour le parsage :

                                    Je découpe l'expression en deux, en respectant la priorité des opérateurs, et des parenthèses. (fonction divide_string).
                                    J'obtiens donc la partie droite et la partie gauche de mon expression (ainsi que l'opérateur), pour construire mon arbre binaire. Et je recommence récursivement, jusqu'à ce que la chaîne ne contienne plus qu'une valeur, ce qui signifie qu'on se trouve à une feuille de l'arbre.

                                    Pour le calcul de l'arbre :

                                    Une fois que l'arbre est construit, le plus dur est fait...Je calcul donc l'arbre récursivement en appliquant les opérateurs adéquats.


                                    TODO :

                                    - Il n'y a pour l'instant AUCUNE gestion d'erreur, je vais essayer d'ajouter ça.
                                    - Peut être revoir l'algorithme de division de la chaîne, ainsi que l'algorithme permettant de supprimer les parenthèses inutiles.
                                    - Ajouter un système de variables.
                                    - Voir si je peux ajouter des fonctions.
                                    - Et on verra pour la suite :) .

                                    PS : vous pouvez utiliser l'option -DDEBUG pour visualiser l'arbre et le découpage de l'expression.

                                    $ ./calc "(5*4-2*(8-(3-(4-4*4)*9)-2)+12)*56"
                                    RESULT : 13552.00
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      1 octobre 2011 à 18:14:57

                                      Ma contribution, pour les expressions « de tous les jours ».

                                      #include <string.h>
                                      #include <stdio.h>
                                      #include <stdlib.h>
                                      #include <ctype.h>
                                      #include <math.h>
                                      
                                      
                                      /* Les nœuds de l'arbre peuvent être soit des opérateurs, soit des
                                       * nombres. */
                                      enum type_noeud
                                      {
                                          OPERATEUR,
                                          NOMBRE 
                                      };
                                      typedef enum type_noeud type_noeud;
                                      
                                      /* Structure contenant un nœud de l'arbre. L'arbre tout entier sera
                                       * représenté par un pointeur sur sa racine. */
                                      struct noeud
                                      {
                                          type_noeud      type;      /* Opérateur ou nombre ? */
                                          int             valeur;    /* Opérateur : code ascii /Nombre : valeur. */
                                          struct noeud *  op_gauche; /* Pour un opérateur : son premier opérande. */
                                          struct noeud *  op_droit;  /* Pour un opérateur : son second opérande. */
                                      };
                                      typedef struct noeud noeud;
                                      /* Évidemment, dans le cas d'un nombre, op_gauche = op_droit = NULL. */
                                      
                                      
                                      
                                      /* Crée un nouvel arbre. */
                                      noeud *creer_arbre(type_noeud type, int valeur, noeud *op_gauche,
                                                         noeud *op_droit)
                                      {
                                          noeud *resultat = malloc(sizeof *resultat);
                                          if(resultat != NULL)
                                          {
                                              resultat->type      = type;
                                              resultat->valeur    = valeur;
                                              resultat->op_gauche = op_gauche;
                                              resultat->op_droit  = op_droit;
                                          }
                                      
                                          return resultat;
                                      }
                                      
                                      /* Libère la mémoire d'un arbre. */
                                      void liberer_arbre(noeud *n)
                                      {
                                          if(n->op_gauche != NULL)
                                          {
                                              liberer_arbre(n->op_gauche);
                                          }
                                      
                                          if(n->op_droit != NULL)
                                          {
                                              liberer_arbre(n->op_droit);
                                          }
                                      
                                          free(n);
                                      }
                                      
                                      
                                      
                                      /* La liste des opérateurs binaires de notre langage, de la plus faible
                                       * priorité à la plus forte. */
                                      const char *op_binaires = "-+/*^";
                                      
                                      
                                      
                                      /* Fonction (récursive) qui analyse une chaîne pour en déduire l'arbre
                                       * correspondant. */
                                      noeud * analyser(char *chaine);
                                      
                                      /* L'idée est de faire une fonction par règle de la grammaire. Chacune des
                                       * fonctions renverra un arbre si on a pu appliquer la règle, ou NULL si non.
                                       */
                                      
                                      
                                      
                                      /* Retourne l'indice de la première parenthèse ouvrante de la chaîne passée
                                       * en argument, mais seulement si tout ce qui est avant cette parenthèse
                                       * est du blanc. Sinon, renvoie -1. */
                                      static int trouver_indice_parenthese_ouvrante(char *chaine)
                                      {
                                          int i;
                                      
                                          /* Trouver la première parenthese ouvrante en parcourant la chaîne. */
                                          for(i = 0; chaine[i] != '('; i++)
                                          {
                                              /* Si la chaîne se termine ou qu'on a trouvé autre chose que du blanc
                                               * avant '(', la règle E->(E) ne pourra être appliquée. On renvoie -1.
                                               */
                                              if(chaine[i] == '\0' || !isspace(chaine[i]))
                                              {
                                                  return -1;
                                              }
                                          }
                                      
                                          return i;
                                      }
                                      
                                      /* Retourne l'indice de la dernière parenthèse fermante de la chaîne passée
                                       * en argument, mais seulement si tout ce qui est après cette parenthèse
                                       * est du blanc. Sinon, renvoie -1. */
                                      static int trouver_indice_parenthese_fermante(char *chaine)
                                      {
                                          /* Trouve la dernière parenthèse fermante en parcourant la toute chaîne
                                           * et en gardant l'index de la dernière ')' rencontrée. */
                                          int i;
                                          int fermante = 0;
                                          for(i = 0; chaine[i] != '\0'; i++)
                                          {
                                              if(chaine[i] == ')')
                                              {
                                                  fermante = i;
                                              }
                                          }
                                          
                                          /* S'il n'y a pas du tout de parenthèse fermante, retourne -1. */
                                          if(fermante == 0)
                                          {
                                              return -1;
                                          }
                                      
                                          /* S'il y a autre chose que du blanc après la dernière ')', la règle ne
                                           * pourra être appliquée : renvoyer -1. */
                                          for(i = fermante+1; chaine[i] != '\0'; i++)
                                          {
                                              if(!isspace(chaine[i]))
                                              {
                                                  return -1;
                                              }
                                          }
                                      
                                          return fermante;
                                      }
                                      
                                      /* Applique la règle E -> (E). */
                                      noeud * trouver_parentheses(char *chaine)
                                      {
                                          noeud *resultat;
                                          /* Ouvrante et fermante sont les indices de la parenthese ourante et
                                           * fermante respectivement. */
                                          int ouvrante = trouver_indice_parenthese_ouvrante(chaine);
                                          int fermante = trouver_indice_parenthese_fermante(chaine); 
                                      
                                          /* Si on n'a pas pu trouver les parenthèses, ou bien s'il y avait du texte
                                           * en dehors des parenthèses, la règle ne peut être appliquée. */
                                          if(ouvrante == -1 || fermante == -1)
                                          {
                                              return NULL;
                                          }
                                      
                                          /* Sinon, on coupe la chaîne de façon à ne garder que l'intérieur des
                                           * parenthèses... */
                                          chaine[fermante] = '\0';
                                          chaine += ouvrante+1;
                                          /* On analyse récursivement... */
                                          resultat = analyser(chaine);
                                          /* Puis on restaure la chaîne dans son état initial. */
                                          chaine[fermante] = ')';
                                      
                                          /* Et on renvoie le résultat de l'analyse ! */
                                          return resultat;
                                      }
                                      
                                      /* Applique une règle de la forme E -> EsE, où s est un symbole, à une chaîne.
                                       * Renvoie le nœud correspondant de l'arbre si on peut, et NULL si la règle
                                       * n'est pas applicable à la chaîne. */
                                      noeud * trouver_operateur(char *chaine, char symbole)
                                      {
                                          noeud *resultat = NULL;
                                          noeud *gauche, *droit;
                                          char *avant_op, *apres_op;
                                      
                                          /* Cherchons le symbole voulu dans la chaîne. S'il ne s'y trouve pas, la
                                           * règle n'est pas applicable. */
                                          char *emplacement_symb = strchr(chaine, symbole);
                                          if(emplacement_symb == NULL)
                                          {
                                              return NULL;
                                          }
                                      
                                          /* On découpe la chaîne en deux sous-chaînes : avant l'opérateur, après
                                           * l'opérateur. On procède en remplaçant notre opérateur par \0 */
                                          avant_op = chaine;
                                          apres_op = emplacement_symb+1;
                                          *emplacement_symb = '\0';
                                      
                                          /* On analyse récursivement les deux sous-chaînes. Si l'une des deux n'est
                                           * pas une expressions, la règle est inapplicable ! */
                                          gauche = analyser(avant_op);
                                          if(gauche != NULL)
                                          {
                                              droit = analyser(apres_op);
                                              if(droit != NULL)
                                              {
                                                  resultat = creer_arbre(OPERATEUR, symbole, gauche, droit);
                                              }
                                              else
                                              {
                                                  liberer_arbre(gauche);
                                              }
                                          }
                                      
                                          /* Enfin, si tout a réussi, on restaure la chaîne dans l'état où
                                           * on l'a trouvée, puis on renvoie l'arbre correspondant. */
                                          *emplacement_symb = symbole;
                                          return resultat; 
                                      }
                                      
                                      /* Applique la règle de la forme E -> N, où N est un nombre. */
                                      noeud * trouver_nombre(char *chaine)
                                      {
                                          size_t i;
                                      
                                          /* Tente de convertir la chaîne en nombre. */
                                          char *fin_du_nombre;
                                          int valeur = (int)(strtol(chaine, &fin_du_nombre, 10));
                                      
                                          /* Si la conversion a échoué, la règle est inapplicable. */
                                          if(fin_du_nombre == chaine)
                                          {
                                              return NULL;
                                          }
                                      
                                          /* S'il reste quelque chose après le nombre, la règle est inapplicable. */
                                          for(i = 0; fin_du_nombre[i] != '\0'; i++)
                                          {
                                              if(!isspace(fin_du_nombre[i]))
                                              {
                                                  return NULL;
                                              }
                                          }
                                      
                                          /* Sinon, tout est OK, on renvoie le noeud. */
                                          return creer_arbre(NOMBRE, valeur, NULL, NULL);
                                      }
                                      
                                      
                                      /* Tente d'appliquer les règles de la grammaire, l'une après l'autre, sur la
                                       * chaîne. Renvoie l'arbre retourné par la première règle applicable, ou bien
                                       * NULL si aucune ne l'est. */
                                      noeud * analyser(char *chaine)
                                      {
                                          size_t i;
                                          noeud *resultat;
                                      
                                          /* En premier lieu, on essaie d'appliquer la règle des parenthèses. */
                                          resultat = trouver_parentheses(chaine);
                                          if(resultat != NULL)
                                          {
                                              return resultat;
                                          }
                                      
                                          /* On tente ensuite d'appliquer les règles des opérateurs binaires. */
                                          for(i = 0; op_binaires[i] != '\0'; i++)
                                          {
                                              resultat = trouver_operateur(chaine, op_binaires[i]);
                                              if(resultat != NULL)
                                              {
                                                  return resultat;
                                              }
                                          }
                                      
                                          /* Enfin, on tente d'appliquer la règle du nombre. */
                                          resultat = trouver_nombre(chaine);
                                      
                                          return resultat;
                                      }
                                      
                                      
                                      /* Calculer la valeur d'un arbre. */ 
                                      int calculer(noeud *n)
                                      {
                                          int resultat;
                                          switch(n->type)
                                          {
                                          /* S'il s'agit d'un nombre, c'est évident. */
                                          case NOMBRE:
                                              resultat = n->valeur;
                                              break;
                                          /* Sinon, on commence par calculer le sous-arbre gauche puis le sous-arbre
                                           * droit, et on applique la bonne opération entre ces valeurs. */
                                          case OPERATEUR:
                                              {
                                                  int op1 = calculer(n->op_gauche);
                                                  int op2 = calculer(n->op_droit);
                                                  switch(n->valeur)
                                                  {
                                                  case '+':
                                                      resultat = op1+op2;
                                                      break;
                                                  case '-':
                                                      resultat = op1-op2;
                                                      break;
                                                  case '*':
                                                      resultat = op1*op2;
                                                      break;
                                                  case '/':
                                                      resultat = op1/op2;
                                                      break;
                                                  case '^':
                                                      resultat = pow(op1, op2);
                                                      break;
                                                  default:
                                                      resultat = 0;
                                                  }
                                              }
                                              break;
                                          default:
                                              resultat = 0;
                                          }
                                      
                                          return resultat;
                                      }
                                      
                                      /* Point d'entrée du programme. */
                                      int main(int argc, char **argv)
                                      {
                                          noeud *a;
                                      
                                          if(argc != 2)
                                          {
                                              printf("Utilisation : %s 'expression'\n", argv[0]);
                                              return 0;
                                          }
                                      
                                          a = analyser(argv[1]);
                                          if(a != NULL)
                                          {
                                              int x = calculer(a);
                                              liberer_arbre(a);
                                      
                                              printf("%d\n", x);
                                          }
                                          else
                                          {
                                              puts("La chaîne donnée n'est pas une expression !");
                                          }
                                      
                                          return 0;
                                      }
                                      


                                      Le code a été peu testé, il doit donc probablement comporter quelques bugs.
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                      J'ai déménagé sur Zeste de savoir — Ex-manager des modérateurs.
                                        1 octobre 2011 à 18:58:51

                                        Tiens, j'avais oublié qu'il existait une fonction pow toute prête dans <math.h>...
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                        Staff désormais retraité.
                                          1 octobre 2011 à 20:07:40

                                          J'ai fait ceci vite fait pour le niveau 2 (pas de gestion des erreurs, la chaîne d'entrée doit être formatée correctement) :

                                          #include <stdio.h>
                                          #include <string.h>
                                          #include <stdlib.h>
                                          
                                          #define EXPR_OPERATEUR 1
                                          #define EXPR_NOMBRE 2
                                          
                                          #define OP_PLUS 1
                                          #define OP_MOINS 2
                                          #define OP_FOIS 3
                                          #define OP_SUR 4
                                          
                                          union U_Expression;
                                          
                                          typedef struct S_Operateur {
                                              unsigned char type;
                                              unsigned char op;
                                              union U_Expression *gauche;
                                              union U_Expression *droite;
                                          } Operateur;
                                          
                                          typedef struct S_Nombre {
                                              unsigned char type;
                                              int valeur;
                                          } Nombre;
                                          
                                          typedef union U_Expression {
                                              unsigned char type;
                                              Operateur operateur;
                                              Nombre nombre;
                                          } Expression;
                                          
                                          Expression *expr_parse(const char *s, size_t end)
                                          {
                                              /* Le but est de trouver l'opérateur à la priorité la plus faible */
                                              size_t pos = 0;
                                              int op = 0; // / * - +
                                          
                                              size_t i = 0;
                                              int stop = 0;
                                          
                                              if(s[0] == '(')
                                              {
                                                  if(end == 0)
                                                      end = strlen(s);
                                                  /*assert(s[end-1] == ')');*/
                                                  return expr_parse(s+1, end-2);
                                              }
                                          
                                              while((end == 0 || i < end) && !stop && s[i] != '\0')
                                              {
                                                  switch(s[i])
                                                  {
                                                  case '+':
                                                      if(op <= 4)
                                                      {
                                                          op = 4;
                                                          pos = i;
                                                      }
                                                      break;
                                                  case '-':
                                                      if(op <= 3)
                                                      {
                                                          op = 3;
                                                          pos = i;
                                                      }
                                                      break;
                                                  case '*':
                                                      if(op <= 2)
                                                      {
                                                          op = 2;
                                                          pos = i;
                                                      }
                                                      break;
                                                  case '/':
                                                      if(op <= 1)
                                                      {
                                                          op = 1;
                                                          pos = i;
                                                      }
                                                      break;
                                                  case '(':
                                                      while(s[++i] != ')'); /* Attention : fin de la chaîne ? */
                                                      break;
                                                  case ')':
                                                      stop = 1;
                                                      break;
                                                  }
                                                  ++i;
                                              }
                                          
                                              /* Nombre */
                                              if(op == 0)
                                              {
                                                  char *endptr;
                                                  Expression *expr = malloc(sizeof(Nombre));
                                                  expr->nombre.type = EXPR_NOMBRE;
                                                  expr->nombre.valeur = strtol(s, &endptr, 10);
                                                  /*if(endptr >= s+end) // Problème */
                                                  return expr;
                                              }
                                          
                                              /* Maintenant on découpe et on récurse */
                                              Expression *expr = malloc(sizeof(Operateur));
                                              expr->operateur.type = EXPR_OPERATEUR;
                                              expr->operateur.gauche = expr_parse(s, pos);
                                              switch(op)
                                              {
                                              case 4:
                                                  expr->operateur.op = OP_PLUS;
                                                  break;
                                              case 3:
                                                  expr->operateur.op = OP_MOINS;
                                                  break;
                                              case 2:
                                                  expr->operateur.op = OP_FOIS;
                                                  break;
                                              case 1:
                                                  expr->operateur.op = OP_SUR;
                                                  break;
                                              }
                                              if(end != 0)
                                                  end -= pos + 1;
                                              expr->operateur.droite = expr_parse(s+pos+1, end);
                                              return expr;
                                          }
                                          
                                          static void _expr_puts(FILE *f, Expression *expr, int toplevel)
                                          {
                                              switch(expr->type)
                                              {
                                              case EXPR_OPERATEUR:
                                                  if(!toplevel)
                                                      fprintf(f, "(");
                                                  _expr_puts(f, expr->operateur.gauche, 0);
                                                  switch(expr->operateur.op)
                                                  {
                                                  case OP_PLUS:
                                                      fprintf(f, "+");
                                                      break;
                                                  case OP_MOINS:
                                                      fprintf(f, "-");
                                                      break;
                                                  case OP_FOIS:
                                                      fprintf(f, "*");
                                                      break;
                                                  case OP_SUR:
                                                      fprintf(f, "/");
                                                      break;
                                                  }
                                                  _expr_puts(f, expr->operateur.droite, 0);
                                                  if(!toplevel)
                                                      fprintf(f, ")");
                                                  break;
                                              case EXPR_NOMBRE:
                                                  fprintf(f, "%d", expr->nombre.valeur);
                                                  break;
                                              }
                                          }
                                          
                                          void expr_puts(FILE *f, Expression *expr)
                                          {
                                              _expr_puts(f, expr, 1);
                                          }
                                          
                                          int expr_eval(Expression *expr)
                                          {
                                              switch(expr->type)
                                              {
                                              case EXPR_OPERATEUR:
                                                  {
                                                      int a = expr_eval(expr->operateur.gauche);
                                                      int b = expr_eval(expr->operateur.droite);
                                                      switch(expr->operateur.op)
                                                      {
                                                      case OP_PLUS:
                                                          return a+b;
                                                      case OP_MOINS:
                                                          return a-b;
                                                      case OP_FOIS:
                                                          return a*b;
                                                      case OP_SUR:
                                                          return a/b;
                                                      }
                                                  }
                                                  break;
                                              case EXPR_NOMBRE:
                                                  return expr->nombre.valeur;
                                              }
                                              return 0;
                                          }
                                          
                                          void expr_free(Expression *expr)
                                          {
                                              switch(expr->type)
                                              {
                                              case EXPR_OPERATEUR:
                                                  expr_free(expr->operateur.gauche);
                                                  expr_free(expr->operateur.droite);
                                                  break;
                                              case EXPR_NOMBRE:
                                                  break;
                                              }
                                              free(expr);
                                          }
                                          
                                          int main(void)
                                          {
                                              static char buf[1024];
                                              fgets(buf, 1024, stdin);
                                              {
                                                  size_t n = strlen(buf);
                                                  if(buf[n-1] == '\n' || buf[n-1] == '\r')
                                                      buf[n-1] = '\0';
                                                  if(buf[n-2] == '\n' || buf[n-2] == '\r')
                                                      buf[n-2] = '\0';
                                              }
                                          
                                              Expression *expr = expr_parse(buf, 0);
                                              expr_puts(stdout, expr);
                                              printf(" = %d\n", expr_eval(expr));
                                              expr_free(expr);
                                              return 0;
                                          }
                                          


                                          Code original sur Gist.Github
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            1 octobre 2011 à 21:41:38

                                            Notre Modérateur ne nous fait pas honneur là. Les codes sont supposés être dans les balises secret ? :-°
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              1 octobre 2011 à 22:15:11

                                              Salut,

                                              Ma participation pour le niveau 1, très basique:


                                              #include <ctype.h>
                                              #include <stdio.h>
                                              #include <stdlib.h>
                                              
                                              #define BUFFER_MAX 255
                                              #define TOKEN_MAX  15
                                              #define STACK_MAX  255
                                              
                                              
                                              static struct stack {
                                              	double data[STACK_MAX];
                                              	double *top;
                                              } s_stack;
                                              
                                              
                                              static void
                                              push_number(double n)
                                              {
                                              	*s_stack.top++ = n;
                                              }
                                              
                                              
                                              static double
                                              pop_number(void)
                                              {
                                              	return *--s_stack.top;
                                              }
                                              
                                              
                                              static void
                                              compute(int op)
                                              {
                                              	double a;
                                              	double b;
                                              
                                              	a = pop_number();
                                              	b = pop_number();
                                              
                                              	switch (op) {
                                              	case '+':
                                              		push_number(a + b);
                                              		break;
                                              
                                              	case '-':
                                              		push_number(a - b);
                                              		break;
                                              
                                              	case '*':
                                              		push_number(a * b);
                                              		break;
                                              
                                              	case '/':
                                              		push_number(a / b);
                                              		break;
                                              	}
                                              }
                                              
                                              
                                              static int
                                              eval(char const *s)
                                              {
                                              	static char token[TOKEN_MAX];
                                              	int n;
                                              	
                                              	s_stack.top = s_stack.data;
                                              	while (sscanf(s, "%s%n", token, &n) > 0) {
                                              		double tmp;
                                              
                                              		if (isdigit(token[0])) {
                                              			if (sscanf(token, "%lf", &tmp) <= 0)
                                              				return 0;
                                              			else
                                              				push_number(tmp);
                                              		} else
                                              			compute(token[0]);
                                              
                                              		s += n;
                                              	}
                                              
                                              	return 1;
                                              }
                                              
                                              
                                              static void
                                              print_result(void)
                                              {
                                              	double result;
                                              
                                              	result = pop_number();
                                              	printf("\t%lf\n", result);
                                              }
                                              
                                              
                                              int
                                              main(void)
                                              {
                                              	static char buffer[BUFFER_MAX];
                                              
                                              	while (fgets(buffer, BUFFER_MAX, stdin) != NULL) {
                                              		if (eval(buffer))
                                              			print_result();
                                              	}
                                              
                                              	return EXIT_SUCCESS;
                                              }
                                              

                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                2 octobre 2011 à 9:27:50

                                                Citation : Lebrian

                                                Notre Modérateur ne nous fait pas honneur là. Les codes sont supposés être dans les balises secret ? :-°



                                                Oups, voilà qui est réparé. Merci ! :)
                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                J'ai déménagé sur Zeste de savoir — Ex-manager des modérateurs.
                                                  2 octobre 2011 à 11:18:24

                                                  Voilà une nouvelle version de mon parseur :

                                                  #include <string.h>
                                                  #include <stdio.h>
                                                  #include <stdlib.h>
                                                  #include <assert.h>
                                                  #include <ctype.h>
                                                  #include <math.h>
                                                  
                                                  enum type;
                                                  union u_type;
                                                  struct token;
                                                  struct tree;
                                                  struct function;
                                                  
                                                  double calc_tree(struct tree *tree);
                                                  struct tree* eval(char *expr, int *error);
                                                  
                                                  /********************************************
                                                   * Types
                                                   *******************************************/
                                                  enum type
                                                  {
                                                     UNDEF,
                                                     OPERATOR,
                                                     VALUE,
                                                     BRACE_LEFT,
                                                     BRACE_RIGHT,
                                                     FUNCTION,
                                                     VARIABLE,
                                                     AFFECTATION
                                                  };
                                                  
                                                  struct operator
                                                  {
                                                     int c;
                                                     double (*callback)(struct tree*, struct tree*);
                                                     int priority;
                                                  };
                                                  
                                                  struct function
                                                  {
                                                     const char *name;
                                                     double (*callback)(struct tree*);
                                                  };
                                                  
                                                  struct variable
                                                  {
                                                     char *name;
                                                     double value;
                                                  };
                                                  
                                                  union u_type
                                                  {
                                                     /* Type OPERATOR */
                                                     int op_id;
                                                     /* Type VALUE */
                                                     double val;
                                                     /* Type FUNCTION */
                                                     int fun_id;
                                                     /* Type VARIABLE */
                                                     int var_id;
                                                  };
                                                  
                                                  struct token
                                                  {
                                                     enum type type;
                                                     union u_type u_type;
                                                  };
                                                  
                                                  struct tree
                                                  {
                                                     struct token token;
                                                  
                                                     struct tree *left;
                                                     struct tree *right;
                                                  };
                                                  
                                                  
                                                  
                                                  /************************************
                                                   * Functions
                                                   ************************************/
                                                  
                                                  
                                                  double fun_sqrt(struct tree *tree)
                                                  {
                                                     return sqrt(calc_tree(tree));
                                                  }
                                                  
                                                  double fun_abs(struct tree *tree)
                                                  {
                                                     return abs(calc_tree(tree));
                                                  }
                                                  
                                                  double fun_cos(struct tree *tree)
                                                  {
                                                     return cos(calc_tree(tree));
                                                  }
                                                  
                                                  struct function Functions[] = 
                                                  {
                                                     {"sqrt", fun_sqrt},
                                                     {"abs", fun_abs},
                                                     {"cos", fun_cos},
                                                  };
                                                  
                                                  /************************************************
                                                   * Operators
                                                   ***********************************************/
                                                  double op_add(struct tree *left, struct tree *right)
                                                  {
                                                     return calc_tree(left) + calc_tree(right);
                                                  }
                                                  
                                                  double op_sub(struct tree *left, struct tree *right)
                                                  {
                                                     return calc_tree(left) - calc_tree(right);
                                                  }
                                                  
                                                  double op_pow(struct tree *left, struct tree *right)
                                                  {
                                                     return pow(calc_tree(left), calc_tree(right));
                                                  }
                                                  
                                                  double op_div(struct tree *left, struct tree *right)
                                                  {
                                                     return calc_tree(left) / calc_tree(right);
                                                  }
                                                  
                                                  double op_mul(struct tree *left, struct tree *right)
                                                  {
                                                     return calc_tree(left) * calc_tree(right);
                                                  }
                                                  
                                                  double op_mod(struct tree *left, struct tree *right)
                                                  {
                                                     return fmod(calc_tree(left), calc_tree(right));
                                                  }
                                                  
                                                  struct operator Operators[] =
                                                  {
                                                     {'+', op_add, 1},
                                                     {'-', op_sub, 1},
                                                     {'*', op_mul, 2},
                                                     {'/', op_div, 2},
                                                     {'%', op_mod, 2},
                                                     {'^', op_pow, 3}
                                                  };
                                                  
                                                  /* Return the priority of the operator ID */
                                                  int priority(int operator_id)
                                                  {
                                                     if(operator_id < 0)
                                                        return -1;
                                                     if(operator_id >= sizeof(Operators)/sizeof(*Operators))
                                                        return -1;
                                                     return Operators[operator_id].priority;
                                                  }
                                                  
                                                  /* Return the ID of the operator, if c is an operator, else -1 */
                                                  int char_to_operator(int c)
                                                  {
                                                     int i;
                                                  
                                                     for(i = 0; i < sizeof(Operators)/sizeof(*Operators); i++)
                                                     {
                                                        if(c == Operators[i].c)
                                                  	 return i;
                                                     }
                                                     return -1;
                                                  }
                                                  /*******************************************
                                                   * Variables
                                                   ******************************************/
                                                  #define MAX_VARIABLES 100
                                                  
                                                  struct variable Variables[MAX_VARIABLES];
                                                  int N_variables = 0;
                                                  
                                                  int check_variable_name(const char *name)
                                                  {
                                                     if(*name == '\0')
                                                        return 0;
                                                  
                                                     while(*name)
                                                     {
                                                        if(!isalpha(*name))
                                                  	 return 0;
                                                        name++;
                                                     }
                                                     return 1;
                                                  }
                                                  int affect_variable(const char *name, double value)
                                                  {
                                                     int i;
                                                     
                                                     for(i = 0; i < N_variables; i++)
                                                     {
                                                        if(!strcmp(Variables[i].name, name))
                                                        {
                                                  	 Variables[i].value = value;
                                                  	 return i;
                                                        }
                                                     }
                                                     if(check_variable_name(name))
                                                     {
                                                        if(N_variables < MAX_VARIABLES)
                                                        {
                                                  	 Variables[N_variables].name = strdup(name);
                                                  	 Variables[N_variables].value = value;
                                                  	 N_variables++;
                                                  	 return N_variables-1;
                                                        }
                                                     }
                                                     return -1;
                                                  }
                                                  
                                                  void free_variables(void)
                                                  {
                                                     int i;
                                                  
                                                     for(i = 0; i < N_variables; i++)
                                                     {
                                                        free(Variables[i].name);
                                                     }
                                                  }
                                                  
                                                  int get_variable_id(const char *name)
                                                  {
                                                     int i;
                                                  
                                                     for(i = 0; i < N_variables; i++)
                                                     {
                                                        if(!strcmp(Variables[i].name, name))
                                                  	 return i;
                                                     }
                                                     return -1;
                                                  }
                                                  
                                                  double get_variable_value(int var_id)
                                                  {
                                                     if(var_id < 0)
                                                        return 0.0;
                                                     if(var_id >= sizeof(Variables)/sizeof(*Variables))
                                                        return 0.0;
                                                  
                                                     return Variables[var_id].value;
                                                  }
                                                  
                                                  /*******************************************
                                                   * Tree miscellaneous functions
                                                   ******************************************/
                                                  void free_tree(struct tree *tree)
                                                  {
                                                     if(tree != NULL)
                                                     {
                                                        free_tree(tree->left);
                                                        free_tree(tree->right);
                                                        free(tree);
                                                     }
                                                  }
                                                  
                                                  struct tree* create_node(struct tree *left, struct token *token, struct tree *right)
                                                  {
                                                     struct tree *node;
                                                  
                                                     if((node = malloc(sizeof(struct tree))) == NULL)
                                                     {
                                                        perror("malloc ");
                                                        exit(EXIT_FAILURE);
                                                     }
                                                     
                                                     node->token = *token;
                                                     node->left = left;
                                                     node->right = right;
                                                  
                                                     return node;
                                                  }
                                                  
                                                  /*******************************************
                                                   * Calc functions
                                                   ******************************************/
                                                  double calc_tree(struct tree *tree)
                                                  {
                                                     assert(tree != NULL);
                                                  
                                                     switch(tree->token.type)
                                                     {
                                                        case OPERATOR:
                                                  	 return Operators[tree->token.u_type.op_id].callback(tree->left, tree->right);
                                                        case VALUE:
                                                  	 return tree->token.u_type.val;
                                                        case FUNCTION:
                                                  	 return Functions[tree->token.u_type.fun_id].callback(tree->left);
                                                        case VARIABLE:
                                                  	 return get_variable_value(tree->token.u_type.var_id);
                                                        case AFFECTATION:
                                                  	 return Variables[tree->right->token.u_type.var_id].value = calc_tree(tree->left);
                                                        default:
                                                  	 return 0.0;
                                                     }
                                                     return 0.0;
                                                  }
                                                  
                                                  double calc(char *expr, int  *error)
                                                  {
                                                     struct tree *tree;
                                                     double result;
                                                  
                                                     tree = eval(expr, error);
                                                  
                                                     if(*error == 0)
                                                        result = calc_tree(tree);
                                                  
                                                     free_tree(tree);
                                                  
                                                     return result;
                                                  }
                                                  
                                                  
                                                  /****************************************
                                                   * Eval functions
                                                   ***************************************/
                                                  /* 
                                                     = Grammar =
                                                     E = expression
                                                     F = function
                                                     R = float
                                                     o = operator
                                                     V = variable
                                                  
                                                     E --> (E)
                                                     E --> F(E)
                                                     E --> V=E
                                                     E --> R
                                                     E --> V
                                                     E --> EoE
                                                  
                                                     o --> [+-/^*%]{1}
                                                     V --> [A-Za-z]+
                                                     F --> [A-Za-z]+
                                                     R --> -{0,1}[0-9.]+
                                                  */
                                                  
                                                  
                                                  /* E --> (E) */
                                                  struct tree*  eval_brace(char **expr, int *error)
                                                  {
                                                     int level, min_level;
                                                     char *ptr;
                                                  
                                                     ptr = *expr;
                                                  
                                                     if(*ptr == '(')
                                                     {
                                                        min_level = 1;
                                                        level = 0;
                                                  
                                                        while(*ptr != '\0')
                                                        {
                                                  	 if(*ptr == '(')
                                                  	    level++;
                                                  	 else if(*ptr == ')')
                                                  	    level--;
                                                  
                                                  	 if(min_level > level && *(ptr+1) != '\0')
                                                  	    min_level = level;
                                                  
                                                  	 ptr++;
                                                        }
                                                        if(min_level == 1 && **expr == '(' && *(ptr-1) == ')')
                                                        {
                                                  	 **expr = '\0';
                                                  	 *(ptr-1) = '\0';
                                                  	 (*expr)++;
                                                  	 return eval(*expr, error);
                                                        }
                                                     }
                                                     return NULL;
                                                  }
                                                  
                                                  /* E --> F(E) */
                                                  struct tree* eval_function(char *expr, int *error)
                                                  {
                                                     int i;
                                                     int len;
                                                     struct token token;
                                                     struct tree *tree = NULL;
                                                  
                                                     for(i = 0; i < sizeof(Functions)/sizeof(*Functions); i++)
                                                     {
                                                        len = strlen(Functions[i].name);
                                                        if(!strncmp(Functions[i].name, expr, len))
                                                        {
                                                  	 token.type = FUNCTION;
                                                  	 token.u_type.fun_id = i;
                                                  	 expr += len;
                                                  	 tree = eval_brace(&expr, error);
                                                  	 if(tree == NULL)
                                                  	 {
                                                  	    return NULL;
                                                  	 }
                                                  	 else
                                                  	 {
                                                  	    return create_node(tree, &token, NULL);
                                                  	 }
                                                        }
                                                     }
                                                     return NULL;
                                                  }
                                                  /* E --> V=E */
                                                  struct tree* eval_affectation(char *expr, int *error)
                                                  {
                                                     struct token token;
                                                     char *ptr;
                                                  
                                                     if((ptr = strchr(expr, '=')) != NULL)
                                                     {
                                                        *ptr = '\0';
                                                        if(affect_variable(expr, 0.0) != -1)
                                                        {
                                                  
                                                  	 token.type = AFFECTATION;
                                                  	 return create_node(eval(ptr+1, error), &token, eval(expr, error));
                                                        }
                                                        *ptr = '=';
                                                     }
                                                     return NULL;
                                                  }
                                                  
                                                  /* E --> EoE */
                                                  struct tree* eval_operator(char *expr, int *error)
                                                  {
                                                     int level;
                                                     int operator_id;
                                                     struct token token;
                                                     char *left, *right;
                                                  
                                                     token.type = UNDEF;
                                                     level = 0;
                                                     left = right = expr;
                                                     
                                                     while(*expr)
                                                     {
                                                        operator_id = char_to_operator(*expr);
                                                        if(operator_id != -1)
                                                        {
                                                  	 if(level == 0)
                                                  	 {
                                                  	    if(token.type == UNDEF || (priority(operator_id) <= priority(token.u_type.op_id)))
                                                  	    {
                                                  	       token.type = OPERATOR;
                                                  	       token.u_type.op_id = operator_id;
                                                  	       right = expr;
                                                  	    }
                                                  	 }
                                                        }
                                                        else if(*expr == '(')
                                                        {
                                                  	 level++;
                                                        }
                                                        else if(*expr == ')')
                                                        {
                                                  	 level--;
                                                        }
                                                        expr++;
                                                     }
                                                  
                                                     if(token.type != UNDEF)
                                                     {
                                                        *(right++) = '\0';
                                                        return create_node(eval(left, error), &token, eval(right, error));
                                                     }
                                                  
                                                     return NULL;
                                                  }
                                                  /* E --> V */
                                                  struct tree* eval_variable(char *expr, int *error)
                                                  {
                                                     int var_id;
                                                     struct token token;
                                                  
                                                     var_id = get_variable_id(expr);
                                                     if(var_id != -1)
                                                     {
                                                        token.type = VARIABLE;
                                                        token.u_type.var_id = var_id;
                                                        return create_node(NULL, &token, NULL);
                                                     }
                                                     return NULL;
                                                  }
                                                  /* E --> R */
                                                  struct tree* eval_float(char *expr, int *error)
                                                  {
                                                     struct token token;
                                                     
                                                     token.type = VALUE;
                                                     token.u_type.val = strtod(expr, &expr);
                                                  
                                                     if(*expr == '\0')
                                                     {
                                                        return create_node(NULL, &token, NULL);
                                                     }
                                                  
                                                     return NULL;
                                                  }
                                                  
                                                  /* Eval all grammars */
                                                  struct tree* eval(char *expr, int *error)
                                                  {
                                                     struct tree *tree = NULL;
                                                  
                                                  #ifdef DEBUG
                                                     printf("-> '%s'\n", expr);
                                                  #endif
                                                  
                                                     if(*expr == '\0')
                                                        *error = 1;
                                                  
                                                     if(*error == 0)
                                                     {  
                                                        tree = eval_brace(&expr, error);
                                                        if(tree == NULL)
                                                        {
                                                  	 tree = eval_function(expr, error);
                                                  	 if(tree == NULL)
                                                  	 {
                                                  	    tree = eval_affectation(expr, error);
                                                  	    if(tree == NULL)
                                                  	    {
                                                  	       tree = eval_float(expr, error);	
                                                  	       if(tree == NULL)
                                                  	       {
                                                  		  tree = eval_variable(expr, error);
                                                  		  if(tree == NULL)
                                                  		  {
                                                  		     tree = eval_operator(expr, error);
                                                  		     if(tree == NULL)
                                                  		     {
                                                  			*error = 1;
                                                  		     }
                                                  		  }
                                                  	       }
                                                  	    } 
                                                  	 }
                                                        }
                                                     }
                                                     return tree;
                                                  }
                                                  /********************************************
                                                   * Internal commands
                                                   *******************************************/
                                                  void show_variables(void)
                                                  {
                                                     int i;
                                                  
                                                     for(i = 0; i < N_variables; i++)
                                                     {
                                                        printf("%s = %.2f\n", Variables[i].name, Variables[i].value);
                                                     }
                                                  }
                                                  
                                                  void show_functions(void)
                                                  {
                                                     int i;
                                                     for(i = 0; i < sizeof(Functions)/sizeof(*Functions); i++)
                                                     {
                                                        printf("--> %s()\n", Functions[i].name);
                                                     }
                                                  }
                                                  
                                                  void delete_variables(void)
                                                  {
                                                     free_variables();
                                                     N_variables = 0;
                                                  }
                                                  
                                                  int handle_commands(const char *buffer)
                                                  {
                                                     if(!strcmp(buffer, "variables"))
                                                        show_variables();
                                                     else if(!strcmp(buffer, "quit"))
                                                        exit(0);
                                                     else if(!strcmp(buffer, "functions"))
                                                        show_functions();
                                                     else if(!strcmp(buffer, "delete_vars"))
                                                        delete_variables();
                                                     else
                                                        return 0;
                                                  
                                                     return 1;
                                                  }
                                                  /********************************************
                                                   * Miscelleanous functions
                                                   *******************************************/
                                                  void chomp(char *buff)
                                                  {
                                                     char *p;
                                                  
                                                     if((p = strchr(buff, '\n')) != NULL)
                                                     {
                                                        *p = '\0';
                                                     }
                                                  }
                                                  
                                                  void print_prompt(void)
                                                  {
                                                     printf("> ");
                                                     fflush(stdout);
                                                  }
                                                  
                                                  /*********************************************
                                                   * Entry point program
                                                   ********************************************/
                                                  int main(void)
                                                  {
                                                     int error;
                                                     double result;
                                                     char buffer[1024];
                                                  
                                                     print_prompt();
                                                     while(fgets(buffer, sizeof(buffer)-1, stdin) != NULL)
                                                     {
                                                        chomp(buffer);
                                                  
                                                        if(handle_commands(buffer) == 0)
                                                        {
                                                  	 error = 0;
                                                  	 printf("%-35s = ", buffer);
                                                  	 result = calc(buffer, &error);
                                                  
                                                  	 if(error == 0)
                                                  	 {
                                                  	    affect_variable("ans", result);
                                                  	    printf("%.2f\n", result);
                                                  	 }
                                                  	 else
                                                  	 {
                                                  	    printf("error !\n");
                                                  	 }
                                                        }
                                                        print_prompt();
                                                     }
                                                  
                                                     free_variables();
                                                     return EXIT_SUCCESS;
                                                  }
                                                  


                                                  - La grammaire utilisée est beaucoup plus claire, et permet d'ajouter des nouvelles règles très facilement.

                                                  - La gestion des erreurs se fait uniquement au niveau de l'évaluation. L'évaluation de l'expression et le calcul de l'arbre est ainsi très distinct.

                                                  - Bon, le type exacte d'erreur n'est pas détecté, mais de ce que j'ai testé (cf plus bas), le parseur détecte toutes les constructions incorrect.

                                                  - Ajout de fonctions (règle E --> F(E))

                                                  - Ajout de variables : affectation (E --> V=E) et utilisation (E --> V)

                                                  - Ajout de quelques commandes internes (functions, variables, delete_vars, quit).

                                                  J'utilise ce fichier pour mes tests :

                                                  5+2*5
                                                  ()+(1)
                                                  (8+)5
                                                  (5*2)+(3*7)^2
                                                  2-(5+7)+7
                                                  7*8+7-7+2-
                                                  (4(+5*4)+1)
                                                  5*(5-(5-3+(5*7)*2-7^2-8*7/(5+2)))
                                                  (5)*(7)+2/7
                                                  )(5+3
                                                  (5*4-2*(8-(3-(4-4*4)*9)-2)+12)*56
                                                  5*2+3%2+8*(5-2)
                                                  abs(-8)
                                                  sqrt(9^2)
                                                  abs(5*(3+2)-7*(2+4))
                                                  abs()
                                                  0.7.8
                                                  abs(5+)
                                                  +15.7
                                                  5+abs(-10)*2
                                                  ans+2
                                                  x=57+3
                                                  5+x*2
                                                  y=
                                                  y=-75
                                                  abs(y*2)


                                                  Et j'obtiens en sortie :

                                                  ./bi_calc < test.txt
                                                  > 5+2*5                               = 15.00
                                                  > ()+(1)                              = error !
                                                  > (8+)5                               = error !
                                                  > (5*2)+(3*7)^2                       = 451.00
                                                  > 2-(5+7)+7                           = -3.00
                                                  > 7*8+7-7+2-                          = error !
                                                  > (4(+5*4)+1)                         = error !
                                                  > 5*(5-(5-3+(5*7)*2-7^2-8*7/(5+2)))   = -50.00
                                                  > (5)*(7)+2/7                         = 35.29
                                                  > )(5+3                               = error !
                                                  > (5*4-2*(8-(3-(4-4*4)*9)-2)+12)*56   = 13552.00
                                                  > 5*2+3%2+8*(5-2)                     = 35.00
                                                  > abs(-8)                             = 8.00
                                                  > sqrt(9^2)                           = 9.00
                                                  > abs(5*(3+2)-7*(2+4))                = 17.00
                                                  > abs()                               = error !
                                                  > 0.7.8                               = error !
                                                  > abs(5+)                             = error !
                                                  > +15.7                               = 15.70
                                                  > 5+abs(-10)*2                        = 25.00
                                                  > ans+2                               = 27.00
                                                  > x=57+3                              = 60.00
                                                  > 5+x*2                               = 125.00
                                                  > y=                                  = error !
                                                  > y=-75                               = -75.00
                                                  > abs(y*2)                            = 150.00


                                                  Voilà, je vais peut être m'attaquer à une gestion d'erreur plus poussée maintenant :) .

                                                  N'hésitez pas à commenter et à tester ;) .
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                  Anonyme
                                                    2 octobre 2011 à 11:26:34

                                                    Ma contribution pour le niveau 1 :

                                                    #include <stdlib.h>
                                                    #include <stdio.h>
                                                    #include <string.h>
                                                    #include <ctype.h>
                                                    #include <math.h>
                                                    
                                                    #define ISOP (expr[i] == '+' || expr[i] ==  '-' || expr[i] == '*' || expr[i] == '/' || expr[i] == '^')
                                                    
                                                    struct cell {
                                                        double value;
                                                        struct cell *next;
                                                    };
                                                    typedef struct cell Cell;
                                                    
                                                    struct stack {
                                                       Cell *first;
                                                    }; typedef struct stack Stack;
                                                    
                                                    /* fonction qui empile un élément à la pile */
                                                    void push(Stack *s, double data)
                                                    {
                                                           Cell *c;
                                                           c = malloc(sizeof *c);
                                                    
                                                           c->value = data;
                                                           c->next = s->first;
                                                    
                                                           s->first = c;
                                                    }
                                                    
                                                    /* fonction qui dépile le dernier élément ajouté */
                                                    double pop(Stack *s) 
                                                    {
                                                       Cell *c;
                                                       double ret = -1.0;
                                                    
                                                       if (s->first == NULL)
                                                       {
                                                          printf("Pile vide.\n");
                                                          return ret;
                                                       }
                                                    
                                                       c = s->first;
                                                       ret = c->value;
                                                       s->first = c->next;
                                                       free(c);
                                                    
                                                       return ret;
                                                    }
                                                    
                                                    int isFloat(double n)
                                                    {
                                                        return ((n-(int)n)>0.0)?1:0;
                                                    }
                                                    
                                                    void del(char *chaine)
                                                    {
                                                        char *p = strchr(chaine, '\n');
                                                    
                                                        if (p)
                                                        {
                                                            *p = 0;
                                                        }
                                                    }
                                                    
                                                    double calc(double n1, double n2, char op)
                                                    {
                                                        double result = 0;
                                                        switch(op)
                                                        {
                                                            case '+':
                                                                result = n1 + n2;
                                                            break;
                                                    
                                                            case '-':
                                                                result = n1 - n2;
                                                            break;
                                                    
                                                            case '*':
                                                                result = n1 * n2;
                                                            break;
                                                    
                                                            case '/':
                                                                result = n1 / n2;
                                                            break;
                                                            case '^':
                                                                result = pow(n1, n2);
                                                            break;
                                                        }
                                                    
                                                        return result;
                                                    }
                                                    
                                                    double parser(const char *expr) 
                                                    {
                                                        Stack stack = { NULL };
                                                        double n1, n2, result, tmp_;
                                                        char tmp[100], *c; 
                                                        int j, i;
                                                    
                                                        for(i = 0; expr[i] != '\0'; i++)
                                                        {
                                                            if(ISOP)
                                                            {
                                                                n2 = pop (&stack);
                                                                n1 = pop (&stack);
                                                    
                                                                result = calc (n1, n2, expr[i]);
                                                                push (&stack, result);
                                                            }
                                                    
                                                            else if (isdigit(expr[i]) != 0)
                                                            {
                                                                j = 0;
                                                    
                                                                while (!isspace(expr[i])) // Pour les nombres à plusieurs chiffres
                                                                {
                                                                    tmp[j] = expr[i];
                                                                    j++, i++;
                                                                }
                                                                
                                                                tmp_ = (strtod(tmp, &c));
                                                                memset(tmp, '\0', strlen(tmp)); // On vide le tampon sinon ça fait caca
                                                    
                                                                push (&stack, tmp_);
                                                            }
                                                            
                                                            else if (isspace(expr[i])) ; 
                                                    
                                                            else {
                                                                fprintf(stderr, "Erreur : opérateur '%c' inconnu.\n\n", expr[i]);
                                                                exit(0);
                                                            }
                                                        }
                                                    
                                                        return pop (&stack);
                                                    }
                                                    
                                                    int main (int argc, char *argv[])
                                                    {
                                                        double result = 0.0;
                                                        char expr[200];
                                                       
                                                        while(1) {
                                                            printf("\n# ");
                                                            fgets(expr, sizeof expr, stdin);
                                                            del(expr); // On supprime le '\n'
                                                    
                                                            result = parser(expr);
                                                            
                                                            if (!isFloat(result))
                                                                printf("-: %d", (int)result);
                                                            else
                                                                printf("-: %f", result);
                                                        }
                                                    
                                                        return 0;
                                                    }
                                                    


                                                    Alors pour commencer, mon implémentation de la pile se décompose en deux structures : une appelée Cell qui représente les cellules et une appelée Stack qui pointe sur la première cellule de la pile. On a ensuite les classiques fonctions push() et pop() qui ajoute un élément en tête de pile et qui retire le premier élément de la pile respectivement.

                                                    Ensuite, dans le main, on lit une expression avec fgets(), on supprime le '\n' de la chaine et on envoie l'expression à la fonction parser(). Une fois que celle-ci a fait son travail, la fonction parser() renvoie le résultat, qui est de type double. On vérifie donc d'abord si la partie décimale n'est pas nulle (genre euh, 24.00000000 quoi) avec la fonction isFloat(). Si c'est le cas on affiche que la partie entière, sinon on garde le résultat tel quel.

                                                    Pour la fonction parser(), elle prend en paramètre l'expression et renvoie le résultat. On parcourt l'expression jusqu'au '\0', à partir de là plusieurs possibilités :
                                                    • expr[i] est un opérateur (soit +, -, *, /, ou ^), on dépile deux nombres avec pop(). Puis on envoie ces deux nombres à la fonction calc() qui prend en paramètres deux nombres et l'opérateur. Elle calcule le tout et nous renvoie le résultat : alors on l'empile.
                                                    • expr[i] est un chiffre : il faut vérifier que ce n'est pas un nombre à plusieurs chiffres. Pour ça, on boucle sur l'expression tant que l'on ne rencontre pas de caractère blanc. On récupère tous les chiffres dans une chaine tmp, ensuite on convertit cette chaine en double et on l'empile.
                                                    • expr[i] est un espace, on ne fait rien.
                                                    • expr[i] n'est rien de tout cela, on affiche une erreur.


                                                    Quelques tests :

                                                    # 302 53 +
                                                    -: 355
                                                    # 500 483 -
                                                    -: 17
                                                    # 13 19 *
                                                    -: 247
                                                    # 63 2 /
                                                    -: 31.500000
                                                    # 63 3 /
                                                    -: 21
                                                    # 0.1 5.4 *
                                                    -: 0.540000
                                                    # 2 5.423 *
                                                    -: 10.846000
                                                    # 134.11 32.45 -
                                                    -: 101.660000
                                                    # 18.1 1574.9 +
                                                    -: 1593
                                                    # 2 5 ^
                                                    -: 32
                                                    # 10 10 ^
                                                    -: 10000000000.000000
                                                    # 1 2 + 4 * 3 +        
                                                    -: 15
                                                    # 3 4 1 2 + * +    
                                                    -: 15
                                                    # 3 2 + 5 * 4 /
                                                    -: 6.250000
                                                    # blblbl
                                                    Erreur : opérateur 'b' inconnu.
                                                    
                                                    [thibault@neow-desktop parser]$


                                                    EDIT DU 17/MAI/2012 :
                                                    Salut !

                                                    Je ressors un vieux code qui date de ce défi, mais que je n'avais pas posté. J'en ai profité pour le modifier un peu. Tout a l'air fonctionnel, mais ça segmente dès que j'entre le nombre "42" (là j'ai pas bien compris pourquoi, mais bon passons :-° ). Voilà donc pour le niveau 2 (y'a aucune gestion des erreurs par contre, la flemme de me replonger totalement dans ce code maintenant) :

                                                    #include <stdlib.h>
                                                    #include <stdio.h>
                                                    #include <ctype.h>
                                                    #include <string.h>
                                                    #include <math.h>
                                                    
                                                    #define T 100
                                                    
                                                    /* Fonctions auxiliaires */
                                                    
                                                    /* Extrait une sous-chaine d'une chaine */
                                                    char *strsub(char *str, int start, int end)
                                                    {
                                                        char *sub = NULL;
                                                        int j = 0, i;
                                                    
                                                        if(str != NULL)
                                                        {
                                                            sub = malloc(sizeof(*sub) * (end - start + 2));
                                                            
                                                            if(sub != NULL) {
                                                                if(start == end)
                                                                {
                                                                    sub[0] = str[start];
                                                                }
                                                                else
                                                                {
                                                                    for(i = 0, j = start; j < end; i++, j++)
                                                                        sub[i] = str[j];
                                                                    sub[i] = '\0';
                                                                }
                                                    
                                                            }
                                                            else
                                                            {
                                                                fprintf (stderr, "Memoire insuffisante\n");
                                                                exit (EXIT_FAILURE);
                                                            }
                                                     
                                                        }
                                                        else
                                                        {
                                                            fprintf (stderr, "Chaine vide.\n");
                                                            exit (EXIT_FAILURE);
                                                        }
                                                    
                                                        return sub;
                                                    }
                                                    
                                                    /* Conversion char -> int */
                                                    int char_to_int(const char str[])
                                                    {
                                                        size_t i;
                                                        int n = 0;
                                                    
                                                        for(i = 0; isdigit(str[i]); ++i)
                                                        {
                                                            n = 10 * n + (str[i] - '0');
                                                        }
                                                    
                                                        return n;
                                                    }
                                                    
                                                    /* Renvoie 1 si la chaine n'est composée que de caractères numériques, 0 si non. */
                                                    int is_number(const char *str)
                                                    {
                                                        size_t i;
                                                      
                                                        for(i = 0; str[i] != '\0'; i++)
                                                        {
                                                            if(!isdigit(str[i]))
                                                                return 0;
                                                        }
                                                        return 1;
                                                    }
                                                    
                                                    /* Supprime le '\n' présent dans la chaine récupérée par fgets et supprime ensuite les espaces */
                                                    void del(char *chaine)
                                                    {
                                                        char *p = strchr(chaine, '\n');
                                                        int i, w = 0;
                                                    
                                                        if (p)
                                                        {
                                                            *p = 0;
                                                        }
                                                    
                                                        for(i=0; chaine[i] != '\0'; i++)
                                                        {
                                                            if(chaine[i] != ' ')
                                                            {
                                                                chaine[w] = chaine[i];
                                                                w++;
                                                            }
                                                        }
                                                    
                                                        chaine[w] = '\0';
                                                    }
                                                    
                                                    void del_char(char* str, int i) //Enleve tous les c de str
                                                    {
                                                       int id_read, id_write;
                                                       id_read = 0;
                                                       id_write = 0;
                                                    
                                                       while (str[id_read] != '\0')
                                                       {
                                                          if (id_read != i)
                                                          {
                                                              str[id_write] = str[id_read];
                                                              id_write++;
                                                          }
                                                          id_read++;
                                                        }
                                                        str[id_write] = '\0';
                                                    }
                                                    
                                                    int indice(char *str, char c)
                                                    {
                                                        size_t i;
                                                        int ret = -1;
                                                    
                                                        for(i = 0; str[i] != '\0'; i++)
                                                        {
                                                            if(str[i] == c)
                                                            {
                                                                ret = i;
                                                                break;
                                                            }
                                                        }
                                                    
                                                        return ret;
                                                    }
                                                    /* ... */
                                                    
                                                    /* Structure de donnée principale, c'est-à-dire l'arbre */
                                                    enum type {
                                                        OPERATEUR,
                                                        OPERANDE
                                                    }; 
                                                    typedef enum type type_e;
                                                    
                                                    struct node {
                                                        type_e type;
                                                        int val;
                                                        struct node *left;
                                                        struct node *right; 
                                                    };
                                                    typedef struct node node;
                                                    
                                                    /* Initialise un nouveau noeud de l'arbre */
                                                    struct node *new_node(type_e type, int value, struct node *p0, struct node *p1)
                                                    {
                                                      struct node *n = malloc(sizeof(struct node));
                                                      if (n == NULL)
                                                      {
                                                          fprintf(stderr, "Erreur fichier %s, ligne %u, ", __FILE__, __LINE__);
                                                          exit(1);
                                                      }
                                                      n->type = type;
                                                      n->val = value;
                                                      n->left = p0;
                                                      n->right = p1;
                                                      return n;
                                                    }
                                                    
                                                    /* ... */
                                                    
                                                    /* La partie principale du parser : une fonction qui construit l'arbre à partir de l'expression mathématique, et une autre qui évalue cet arbre */
                                                    
                                                    /* Retourne la position de l'opérateur le moins prioritaire de l'expression passée en argument */
                                                    int to_op(char *expr, int *test)
                                                    {
                                                        int i, par = 0;
                                                        char *pch;
                                                    
                                                        if(1)
                                                        {
                                                            /* On recherche d'abord un - ou un + */
                                                            for(i=strlen(expr); i > 0 && expr[i] != '+' && expr[i] != '-'; i--)
                                                            {
                                                                 /* On évite les parenthèses : */
                                                                 if(expr[i] == ')') {
                                                                     for(i--, par = 1; par != 0; i--) {
                                                                         if(expr[i] == ')') par++;
                                                                         if(expr[i] == '(') par--;
                                                                     }
                                                                     i++;
                                                                 }
                                                            }
                                                            /* Si on en a trouvé un */
                                                            if(i > 0)
                                                            {
                                                                *test = 1;
                                                                return i;
                                                            }    
                                                            else 
                                                            {
                                                                /* Sinon, on recherche un * ou un / */
                                                                for(i=strlen(expr); i > 0 && expr[i] != '*' && expr[i] != '/'; i--)
                                                                {
                                                                    /* On évite les parenthèses */
                                                                    if(expr[i] == ')') {
                                                                        for(i--, par = 1; par != 0; i--) {
                                                                            if(expr[i] == ')') par++;
                                                                            if(expr[i] == '(') par--;
                                                                        }
                                                                        i++;
                                                                    }
                                                                }
                                                    
                                                                if(i > 0) {
                                                                    *test = 2;
                                                                    return i;
                                                                }
                                                                else
                                                                {
                                                                    /* Sinon, on recherche une puissance (^) */
                                                                    for(i=strlen(expr); i > 0 && expr[i] != '^'; i--)
                                                                    {
                                                                        if(expr[i] == ')') {
                                                                            for(i--, par = 1; par != 0; i--) {
                                                                                if(expr[i] == ')') par++;
                                                                                if(expr[i] == '(') par--;
                                                                            }
                                                                            i++;
                                                                        }
                                                                    }
                                                                    if(i > 0)
                                                                    {
                                                                        *test = 3;
                                                                        return i;
                                                                    }
                                                                    /* Si tous les recherches ont échoués, il ne reste que les parenthèses */
                                                                    else if(expr[0] == '(' && expr[strlen(expr)-1] == ')')
                                                                    {
                                                                        *test = 4;
                                                                        /* La première parenthèse ouvrante se trouve au tout début de la chaine : */
                                                                        return 0;
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    
                                                        printf("Expression invalide\n");
                                                        exit(1);
                                                    }
                                                    
                                                    /* Evalue un arbre de manière récursive */
                                                    int evalue_tree(node *node)
                                                    {
                                                        if(node->type == OPERANDE)
                                                            return node->val;
                                                        else {
                                                            switch(node->val)
                                                            {
                                                                case '+':
                                                                    return evalue_tree(node->left) + evalue_tree(node->right);
                                                                break;
                                                             
                                                                case '-':
                                                                    return evalue_tree(node->left) - evalue_tree(node->right);
                                                                break;
                                                         
                                                                case '*':
                                                                    return evalue_tree(node->left) * evalue_tree(node->right);
                                                                break;
                                                    
                                                                case '/':
                                                                    return evalue_tree(node->left) / evalue_tree(node->right);
                                                                break;
                                                            
                                                                case '^':
                                                                    return pow(evalue_tree(node->left), evalue_tree(node->right));
                                                                break;
                                                            }
                                                        }
                                                    }
                                                    
                                                    /* Construit un arbre à partir d'une expression */
                                                    node *cons(char *expr)
                                                    {
                                                        node *node;
                                                        int nbr, i = 0, op = 0;
                                                        char left[T], right[T];
                                                    
                                                        if(is_number(expr)) // C'est fini
                                                        {
                                                            nbr = char_to_int(expr);
                                                            node = new_node(OPERATEUR, nbr, NULL, NULL);
                                                    
                                                            return node;
                                                        }
                                                        else 
                                                        {
                                                            i = to_op(expr, &op);
                                                            /* Si on a rien trouvé en dehors des () */ 
                                                            if(op == 4)
                                                            {
                                                                sprintf(right, "%s", strsub(expr, i+1, strlen(expr)-1));
                                                                node = new_node(OPERATEUR, expr[i], NULL, NULL);
                                                                node->right = cons(right);
                                                            }
                                                            /* Sinon : */
                                                            else if(i != 0 && op != 0)
                                                            {
                                                                /* On découpe la chaine en deux partie */
                                                                sprintf(left, "%s", strsub(expr, 0, i));
                                                                sprintf(right, "%s", strsub(expr, i+1, strlen(expr)));
                                                    
                                                                /* On créé un nouveau noeud */
                                                                node = new_node(OPERATEUR, expr[i], NULL, NULL);
                                                    
                                                                /* On fait un appel récursif pour la suite */
                                                                node->left = cons(left);
                                                                node->right = cons(right);
                                                    
                                                                return node;
                                                            }
                                                            else 
                                                            {
                                                                printf("Expression incorrecte.");
                                                                exit(1);
                                                            }
                                                        }
                                                    }
                                                    
                                                    
                                                    /* Le point d'entrée du programme. Récupère l'expression mathématique sur l'entrée standard */ 
                                                    int main(void) 
                                                    {
                                                        node *n1;
                                                        char expr[100];
                                                    
                                                        while(1) {
                                                            printf("\n# ");
                                                            fgets(expr, sizeof expr, stdin);
                                                            del(expr); // On supprime le '\n'
                                                            n1 = cons(expr);
                                                            
                                                            
                                                            printf("-: %d", evalue_tree(n1));
                                                        }
                                                    
                                                        return 0;
                                                    }
                                                    
                                                    /* ... */
                                                    


                                                    Les tests :

                                                    # (50+25) * 3
                                                    -: 225
                                                    # ((1)+(1)+(1))
                                                    -: 3
                                                    # 1000^3
                                                    -: 1000000000
                                                    # 25-2*3
                                                    -: 19
                                                    # 3 / 2 + 5 * 4
                                                    -: 21
                                                    # 42 + 42
                                                    Erreur de segmentation (core dumped)
                                                    [thibault@neow-desktop parser]$

                                                    12:41:28 neowillow | 42 C LUNIVER
                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      2 octobre 2011 à 11:55:27

                                                      @GuilOooo : J'ai un peu modifié ton programme pour qu'il lise sur STDIN, et apparement il y a pas mal de constructions que ton parseur ne gère pas :


                                                      $ ./test < test.txt
                                                      5+2*5                               = 15
                                                      ()+(1)                              = La chaîne donnée n'est pas une expression !
                                                      (8+)5                               = La chaîne donnée n'est pas une expression !
                                                      (5*2)+(3*7)^2                       = 451
                                                      2-(5+7)+7                           = La chaîne donnée n'est pas une expression !
                                                      7*8+7-7+2-                          = La chaîne donnée n'est pas une expression !
                                                      (4(+5*4)+1)                         = La chaîne donnée n'est pas une expression !
                                                      5*(5-(5-3+(5*7)*2-7^2-8*7/(5+2)))   = La chaîne donnée n'est pas une expression !
                                                      (5)*(7)+2/7                         = La chaîne donnée n'est pas une expression !
                                                      )(5+3                               = La chaîne donnée n'est pas une expression !
                                                      (5*4-2*(8-(3-(4-4*4)*9)-2)+12)*56   = La chaîne donnée n'est pas une expression !
                                                      0.7.8                               = La chaîne donnée n'est pas une expression !
                                                      +15.7                               = La chaîne donnée n'est pas une expression !


                                                      @neowillow : Je ne sais pas pour quelle raison, mais je n'arrive pas à tester ton parseur :


                                                      # 5+2
                                                      Erreur : opérateur '°' inconnu.

                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        2 octobre 2011 à 11:57:58

                                                        Citation : Tosh

                                                        @neowillow : Je ne sais pas pour quelle raison, mais je n'arrive pas à tester ton parseur


                                                        Citation : neowillow

                                                        Ma contribution pour le niveau 1


                                                        Niveau 1 = NPI, c'est normal que ça ne fonctionne pas. ;)
                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                          2 octobre 2011 à 12:09:27

                                                          En fait à chaque défis posté, le "boss" à battre correspond à *osh :D

                                                          Sans rire y'a quelques features dans la version de Tosh!
                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                            3 octobre 2011 à 15:33:14

                                                            Citation : remram44

                                                            J'ai fait ceci vite fait pour le niveau 2 (pas de gestion des erreurs, la chaîne d'entrée doit être formatée correctement)



                                                            J'ai ajouté la gestion des erreurs, et j'utilise une table de pointeurs de fonction pour le style.

                                                            #include <stdio.h>
                                                            #include <string.h>
                                                            #include <stdlib.h>
                                                            
                                                            #define EXPR_OPERATEUR 1
                                                            #define EXPR_NOMBRE 2
                                                            
                                                            union U_Expression;
                                                            
                                                            typedef struct S_Operateur {
                                                            
                                                                unsigned char type;
                                                                unsigned char op;
                                                                union U_Expression *gauche;
                                                                union U_Expression *droite;
                                                            
                                                            } Operateur;
                                                            
                                                            typedef struct S_Nombre {
                                                            
                                                                unsigned char type;
                                                                int valeur;
                                                            
                                                            } Nombre;
                                                            
                                                            typedef union U_Expression {
                                                            
                                                                unsigned char type;
                                                                Operateur operateur;
                                                                Nombre nombre;
                                                            
                                                            } Expression;
                                                            
                                                            struct _expr_operateur {
                                                                char symbole;
                                                                int (*fonction)(int, int);
                                                                int priorite;
                                                            };
                                                            
                                                            static int _f_plus(int a, int b) { return a+b; }
                                                            static int _f_moins(int a, int b) { return a-b; }
                                                            static int _f_fois(int a, int b) { return a*b; }
                                                            static int _f_sur(int a, int b) { return a/b; }
                                                            
                                                            static struct _expr_operateur _operateurs[] = {
                                                                {'+', _f_plus, 4},
                                                                {'-', _f_moins, 3},
                                                                {'*', _f_fois, 2},
                                                                {'/', _f_sur, 1}
                                                            };
                                                            
                                                            static const size_t _nb_operateurs =
                                                                    sizeof(_operateurs)/sizeof(struct _expr_operateur);
                                                            
                                                            Expression *expr_parse(const char *s, size_t end)
                                                            {
                                                                /* Le but est de trouver l'opérateur à la priorité la plus faible */
                                                                size_t pos = 0;
                                                                int op = -1;
                                                                int prio = 0;
                                                            
                                                                size_t i = 0;
                                                            
                                                                while((end == 0 || i < end) && s[i] != '\0')
                                                                {
                                                                    size_t j;
                                                                    if(s[i] == '(')
                                                                        while(s[++i] != ')')
                                                                        {
                                                                            if(s[i] == '\0')
                                                                            {
                                                                                fprintf(stderr, "Parenthese fermante manquante !\n");
                                                                                return NULL;
                                                                            }
                                                                        }
                                                                    else if(s[i] == ')')
                                                                        break;
                                                                    else for(j = 0; j < _nb_operateurs; ++j)
                                                                    {
                                                                        if(s[i] == _operateurs[j].symbole
                                                                         && prio <= _operateurs[j].priorite)
                                                                        {
                                                                            prio = _operateurs[j].priorite;
                                                                            op = j;
                                                                            pos = i;
                                                                        }
                                                                    }
                                                                    ++i;
                                                                }
                                                            
                                                                /* Pas d'opérateur au plus haut niveau */
                                                                if(op == -1)
                                                                {
                                                                    /* Tout est parenthésé ? */
                                                                    if(s[0] == '(')
                                                                    {
                                                                        if(end == 0)
                                                                            end = strlen(s);
                                                                        if(s[end-1] != ')')
                                                                        {
                                                                            fprintf(stderr, "Parenthese fermante manquante !\n");
                                                                            return NULL;
                                                                        }
                                                                        if(end <= 1)
                                                                        {
                                                                            fprintf(stderr, "Expression parenthesee invalide !\n");
                                                                            return NULL;
                                                                        }
                                                                        return expr_parse(s+1, end-2);
                                                                    }
                                                                    /* C'est un nombre */
                                                                    else
                                                                    {
                                                                        char *endptr;
                                                                        Expression *expr = malloc(sizeof(Nombre));
                                                                        expr->nombre.type = EXPR_NOMBRE;
                                                                        expr->nombre.valeur = strtol(s, &endptr, 10);
                                                                        if( (end != 0 && endptr != s+end) || (end == 0 && *endptr != '\0')
                                                                         || endptr == s)
                                                                        {
                                                                            fprintf(stderr, "Constante numerique invalide !\n");
                                                                            free(expr);
                                                                            return NULL;
                                                                        }
                                                                        return expr;
                                                                    }
                                                                }
                                                            
                                                                /* Maintenant on découpe et on récurse */
                                                                {
                                                                    Expression *a, *b;
                                                                    Expression *expr;
                                                                    a = expr_parse(s, pos);
                                                                    if(a == NULL)
                                                                        return NULL;
                                                                    if(end != 0)
                                                                        end -= pos + 1;
                                                                    b = expr_parse(s+pos+1, end);
                                                                    if(b == NULL)
                                                                        return NULL;
                                                                    
                                                                    expr = malloc(sizeof(Operateur));
                                                                    expr->operateur.type = EXPR_OPERATEUR;
                                                                    expr->operateur.op = op;
                                                                    expr->operateur.gauche = a;
                                                                    expr->operateur.droite = b;
                                                            
                                                                    return expr;
                                                                }
                                                            }
                                                            
                                                            static void _expr_puts(FILE *f, Expression *expr, int toplevel)
                                                            {
                                                                switch(expr->type)
                                                                {
                                                                case EXPR_OPERATEUR:
                                                                    if(!toplevel)
                                                                        fprintf(f, "(");
                                                                    _expr_puts(f, expr->operateur.gauche, 0);
                                                                    fprintf(f, "%c", _operateurs[expr->operateur.op].symbole);
                                                                    _expr_puts(f, expr->operateur.droite, 0);
                                                                    if(!toplevel)
                                                                        fprintf(f, ")");
                                                                    break;
                                                                case EXPR_NOMBRE:
                                                                    fprintf(f, "%d", expr->nombre.valeur);
                                                                    break;
                                                                }
                                                            }
                                                            
                                                            void expr_puts(FILE *f, Expression *expr)
                                                            {
                                                                _expr_puts(f, expr, 1);
                                                            }
                                                            
                                                            int expr_eval(Expression *expr)
                                                            {
                                                                switch(expr->type)
                                                                {
                                                                case EXPR_OPERATEUR:
                                                                    {
                                                                        int a = expr_eval(expr->operateur.gauche);
                                                                        int b = expr_eval(expr->operateur.droite);
                                                                        return _operateurs[expr->operateur.op].fonction(a, b);
                                                                    }
                                                                    break;
                                                                case EXPR_NOMBRE:
                                                                    return expr->nombre.valeur;
                                                                }
                                                                return 0;
                                                            }
                                                            
                                                            void expr_free(Expression *expr)
                                                            {
                                                                switch(expr->type)
                                                                {
                                                                case EXPR_OPERATEUR:
                                                                    expr_free(expr->operateur.gauche);
                                                                    expr_free(expr->operateur.droite);
                                                                    break;
                                                                case EXPR_NOMBRE:
                                                                    break;
                                                                }
                                                                free(expr);
                                                            }
                                                            
                                                            int main(void)
                                                            {
                                                                Expression *expr;
                                                                static char buf[1024];
                                                                fgets(buf, 1024, stdin);
                                                                {
                                                                    size_t n = strlen(buf);
                                                                    if(buf[n-1] == '\n' || buf[n-1] == '\r')
                                                                        buf[n-1] = '\0';
                                                                    if(buf[n-2] == '\n' || buf[n-2] == '\r')
                                                                        buf[n-2] = '\0';
                                                                }
                                                            
                                                                expr = expr_parse(buf, 0);
                                                                if(expr != NULL)
                                                                {
                                                                    expr_puts(stdout, expr);
                                                                    printf(" = %d\n", expr_eval(expr));
                                                                    expr_free(expr);
                                                                    return 0;
                                                                }
                                                                return 1;
                                                            }
                                                            


                                                            Code original sur Gist.Github
                                                            • Partager sur Facebook
                                                            • Partager sur Twitter

                                                            [FAIT][Défis] #4 : Faisons une vraie calculette !

                                                            × 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