Recent Changes - Search:

My Projects

Courses

Writings

Source Code

Social Networks

Live Traffic !

Évolution 2 : If/Else

Créer son propre langage de programmation de A à Z

<< Évolution 1 : Opérateurs de comparaison | Évolution 2 : If/Else | Évolution 3 : Les commentaires >>

Il est temps d'implémenter les conditions. Comme tout langage de programmation qui se respecte, le développeur doit pouvoir exécuter des instructions sous certaines conditions. Je vais profiter de cette évolution pour ajouter l'opérateur unaire "-" que j'ai complètement zappé. Il y a le "-" de la soustraction, la différence entre deux nombres, mais aussi le "-" comme opérateur unaire, qui indique le caractère négatif du nombre. Et celui-là je ne l'ai jusqu'ici pas encore mis.

Évolution 2 du langage :

⚠ (:source lang=bnf linenum:) <condition_si> ::= "si" <expression_booleenne> "alors" <code > <:vspace> <condition_sinon> ::= "sinon" <code > <:vspace> <condition> ::= <condition_si> [<condition_sinon>] ";" <:vspace> <code > ::= <code > | <condition> <:vspace> <expression_arithmetique> ::= <expression_arithmetique> | "-" <expression_arithmetique> (:sourcend:) On ajoute les nouveaux lexèmes à l'analyseur lexical : ⚠ (:source lang=c header="lexique_simple.lex" linestart=22 linenum:) "si" {return TOK_SI;} <:vspace> "alors" {return TOK_ALORS;} <:vspace> "sinon" {return TOK_SINON;} (:sourcend:) On met à jour l'analyseur syntaxique : ⚠ (:source lang=c header="syntaxe_simple.y" linenum:) %{ <:vspace> #include "simple.h" bool error_syntaxical=false; bool error_semantical=false; /* Notre table de hachage */ GHashTable* table_variable; <:vspace> /* Notre structure Variable qui a comme membre le type et un pointeur generique vers la valeur */ typedef struct Variable Variable; <:vspace> struct Variable{ char* type; GNode* value; }; <:vspace> %} <:vspace> /* L'union dans Bison est utilisee pour typer nos tokens ainsi que nos non terminaux. Ici nous avons declare une union avec trois types : nombre de type int, texte de type pointeur de char (char*) et noeud d'arbre syntaxique (AST) de type (GNode*) */ <:vspace> %union { long nombre; char* texte; GNode* noeud; } <:vspace> /* Nous avons ici les operateurs, ils sont definis par leur ordre de priorite. Si je definis par exemple la multiplication en premier et l'addition apres, le + l'emportera alors sur le * dans le langage. Les parenthese sont prioritaires avec %right */ <:vspace> %left TOK_PLUS TOK_MOINS /* +- */ %left TOK_MUL TOK_DIV /* /* */ %left TOK_ET TOK_OU TOK_NON /* et ou non */ %left TOK_EQU TOK_DIFF TOK_SUP TOK_INF TOK_SUPEQU TOK_INFEQU /* comparaisons */ %right TOK_PARG TOK_PARD /* () */ <:vspace> /* Nous avons la liste de nos expressions (les non terminaux). Nous les typons tous en noeud de l'arbre syntaxique (GNode*) */ <:vspace> %type<noeud> code %type<noeud> instruction %type<noeud> condition %type<noeud> condition_si %type<noeud> condition_sinon %type<noeud> variable_arithmetique %type<noeud> variable_booleenne %type<noeud> affectation %type<noeud> affichage %type<noeud> expression_arithmetique %type<noeud> expression_booleenne %type<noeud> addition %type<noeud> soustraction %type<noeud> multiplication %type<noeud> division <:vspace> /* Nous avons la liste de nos tokens (les terminaux de notre grammaire) */ <:vspace> %token<nombre> TOK_NOMBRE %token TOK_VRAI /* true */ %token TOK_FAUX /* false */ %token TOK_AFFECT /* = */ %token TOK_FINSTR /* ; */ %token TOK_IN /* dans */ %token TOK_CROG TOK_CROD /* [] */ %token TOK_AFFICHER /* afficher */ %token<texte> TOK_VARB /* variable booleenne */ %token<texte> TOK_VARE /* variable arithmetique */ %token TOK_SI /* si */ %token TOK_ALORS /* alors */ %token TOK_SINON /* sinon */ <:vspace> %% <:vspace> /* Nous definissons toutes les regles grammaticales de chaque non terminal de notre langage. Par defaut on commence a definir l'axiome, c'est a dire ici le non terminal code. Si nous le definissons pas en premier nous devons le specifier en option dans Bison avec %start */ <:vspace> entree: code{ genere_code($1); g_node_destroy($1); }; <:vspace> code: %empty{$$=g_node_new((gpointer)CODE_VIDE);} | code instruction{ printf("Resultat : C'est une instruction valide !\n\n"); $$=g_node_new((gpointer)SEQUENCE); g_node_append($$,$1); g_node_append($$,$2); } | code error{ fprintf(stderr,"\tERREUR : Erreur de syntaxe a la ligne %d.\n",lineno); error_syntaxical=true; }; <:vspace> instruction: affectation{ printf("\tInstruction type Affectation\n"); $$=$1; } | affichage{ printf("\tInstruction type Affichage\n"); $$=$1; } | condition{ printf("Condition si/sinon\n"); $$=$1; }; <:vspace> variable_arithmetique: TOK_VARE{ printf("\t\t\tVariable entiere %s\n",$1); $$=g_node_new((gpointer)VARIABLE); g_node_append_data($$,strdup($1)); }; <:vspace> variable_booleenne: TOK_VARB{ printf("\t\t\tVariable booleenne %s\n",$1); $$=g_node_new((gpointer)VARIABLE); g_node_append_data($$,strdup($1)); }; <:vspace> condition: condition_si TOK_FINSTR{ printf("\tCondition si\n"); $$=g_node_new((gpointer)CONDITION_SI); g_node_append($$,$1); } | condition_si condition_sinon TOK_FINSTR{ printf("\tCondition si/sinon\n"); $$=g_node_new((gpointer)CONDITION_SI_SINON); g_node_append($$,$1); }; <:vspace> condition_si: TOK_SI expression_booleenne TOK_ALORS code{ $$=g_node_new((gpointer)SI); g_node_append($$,$2); g_node_append($$,$4); }; <:vspace> condition_sinon: TOK_SINON code{ $$=g_node_new((gpointer)SINON); g_node_append($$,$2); }; <:vspace> affectation: variable_arithmetique TOK_AFFECT expression_arithmetique TOK_FINSTR{ /* $1 est la valeur du premier non terminal. Ici c'est la valeur du non terminal variable. $3 est la valeur du 2nd non terminal. */ printf("\t\tAffectation sur la variable\n"); Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($1,0)->data); if(var==NULL){ /* On cree une Variable et on lui affecte le type que nous connaissons et la valeur */ var=malloc(sizeof(Variable)); if(var!=NULL){ var->type=strdup("entier"); var->value=$3; /* On l'insere dans la table de hachage (cle: <nom_variable> / valeur: <(type,valeur)>) */ if(g_hash_table_insert(table_variable,g_node_nth_child($1,0)->data,var)){ $$=g_node_new((gpointer)AFFECTATIONE); g_node_append($$,$1); g_node_append($$,$3); }else{ fprintf(stderr,"ERREUR - PROBLEME CREATION VARIABLE !\n"); exit(-1); } }else{ fprintf(stderr,"ERREUR - PROBLEME ALLOCATION MEMOIRE VARIABLE !\n"); exit(-1); } }else{ $$=g_node_new((gpointer)AFFECTATION); g_node_append($$,$1); g_node_append($$,$3); } } | variable_booleenne TOK_AFFECT expression_booleenne TOK_FINSTR{ /* $1 est la valeur du premier non terminal. Ici c'est la valeur du non terminal variable. $3 est la valeur du 2nd non terminal. */ printf("\t\tAffectation sur la variable\n"); Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($1,0)->data); if(var==NULL){ /* On cree une Variable et on lui affecte le type que nous connaissons et la valeur */ var=malloc(sizeof(Variable)); if(var!=NULL){ var->type=strdup("booleen"); var->value=$3; /* On l'insere dans la table de hachage (cle: <nom_variable> / valeur: <(type,valeur)>) */ if(g_hash_table_insert(table_variable,g_node_nth_child($1,0)->data,var)){ $$=g_node_new((gpointer)AFFECTATIONB); g_node_append($$,$1); g_node_append($$,$3); }else{ fprintf(stderr,"ERREUR - PROBLEME CREATION VARIABLE !\n"); exit(-1); } }else{ fprintf(stderr,"ERREUR - PROBLEME ALLOCATION MEMOIRE VARIABLE !\n"); exit(-1); } }else{ $$=g_node_new((gpointer)AFFECTATION); g_node_append($$,$1); g_node_append($$,$3); } }; <:vspace> affichage: TOK_AFFICHER expression_arithmetique TOK_FINSTR{ printf("\t\tAffichage de la valeur de l'expression arithmetique\n"); $$=g_node_new((gpointer)AFFICHAGEE); g_node_append($$,$2); } | TOK_AFFICHER expression_booleenne TOK_FINSTR{ printf("\t\tAffichage de la valeur de l'expression booleenne\n"); $$=g_node_new((gpointer)AFFICHAGEB); g_node_append($$,$2); }; <:vspace> <:vspace> expression_arithmetique: TOK_NOMBRE{ printf("\t\t\tNombre : %ld\n",$1); /* Comme le token TOK_NOMBRE est de type entier et que on a type expression_arithmetique comme du texte, il nous faut convertir la valeur en texte. */ int length=snprintf(NULL,0,"%ld",$1); char* str=malloc(length+1); snprintf(str,length+1,"%ld",$1); $$=g_node_new((gpointer)ENTIER); g_node_append_data($$,strdup(str)); free(str); } | variable_arithmetique{ /* On recupere un pointeur vers la structure Variable */ Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($1,0)->data); /* Si on a trouve un pointeur valable */ if(var!=NULL){ /* On verifie que le type est bien un entier - Inutile car impose a l'analyse syntaxique */ if(strcmp(var->type,"entier")==0){ $$=$1; }else{ fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Type incompatible (entier attendu - valeur : %s) !\n",lineno,(char*)g_node_nth_child($1,0)->data); error_semantical=true; } /* Sinon on conclue que la variable n'a jamais ete declaree car absente de la table */ }else{ fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Variable %s jamais declaree !\n",lineno,(char*)g_node_nth_child($1,0)->data); error_semantical=true; } } | addition{ $$=$1; } | soustraction{ $$=$1; } | multiplication{ $$=$1; } | division{ $$=$1; } | TOK_PLUS expression_arithmetique{ $$=$2; } | TOK_MOINS expression_arithmetique{ printf("\t\t\tOperation unaire negation\n"); $$=g_node_new((gpointer)NEGATIF); g_node_append($$,$2); } | TOK_PARG expression_arithmetique TOK_PARD{ printf("\t\t\tC'est une expression artihmetique entre parentheses\n"); $$=g_node_new((gpointer)EXPR_PAR); g_node_append($$,$2); }; <:vspace> expression_booleenne: TOK_VRAI{ printf("\t\t\tBooleen Vrai\n"); $$=g_node_new((gpointer)VRAI); } | TOK_FAUX{ printf("\t\t\tBooleen Faux\n"); $$=g_node_new((gpointer)FAUX); } | variable_booleenne{ /* On recupere un pointeur vers la structure Variable */ Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($1,0)->data); /* Si on a trouve un pointeur valable */ if(var!=NULL){ /* On verifie que le type est bien un entier - Inutile car impose a l'analyse syntaxique */ if(strcmp(var->type,"booleen")==0){ $$=$1; }else{ fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Type incompatible (booleen attendu - valeur : %s) !\n",lineno,(char*)g_node_nth_child($1,0)->data); error_semantical=true; } /* Sinon on conclue que la variable n'a jamais ete declaree car absente de la table */ }else{ fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Variable %s jamais declaree !\n",lineno,(char*)g_node_nth_child($1,0)->data); error_semantical=true; } } | TOK_NON expression_booleenne{ printf("\t\t\tOperation booleenne Non\n"); $$=g_node_new((gpointer)NON); g_node_append($$,$2); } | expression_booleenne TOK_ET expression_booleenne{ printf("\t\t\tOperation booleenne Et\n"); $$=g_node_new((gpointer)ET); g_node_append($$,$1); g_node_append($$,$3); } | expression_booleenne TOK_OU expression_booleenne{ printf("\t\t\tOperation booleenne Ou\n"); $$=g_node_new((gpointer)OU); g_node_append($$,$1); g_node_append($$,$3); } | TOK_PARG expression_booleenne TOK_PARD{ printf("\t\t\tC'est une expression booleenne entre parentheses\n"); $$=g_node_new((gpointer)EXPR_PAR); g_node_append($$,$2); } | expression_booleenne TOK_EQU expression_booleenne{ printf("\t\t\tOperateur d'egalite ==\n"); $$=g_node_new((gpointer)EGALITE); g_node_append($$,$1); g_node_append($$,$3); } | expression_booleenne TOK_DIFF expression_booleenne{ printf("\t\t\tOperateur d'inegalite !=\n"); $$=g_node_new((gpointer)DIFFERENT); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_EQU expression_arithmetique{ printf("\t\t\tOperateur d'egalite ==\n"); $$=g_node_new((gpointer)EGALITE); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_DIFF expression_arithmetique{ printf("\t\t\tOperateur d'inegalite !=\n"); $$=g_node_new((gpointer)DIFFERENT); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_SUP expression_arithmetique{ printf("\t\t\tOperateur de superiorite >\n"); $$=g_node_new((gpointer)SUPERIEUR); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_INF expression_arithmetique{ printf("\t\t\tOperateur d'inferiorite <\n"); $$=g_node_new((gpointer)INFERIEUR); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_SUPEQU expression_arithmetique{ printf("\t\t\tOperateur >=\n"); $$=g_node_new((gpointer)SUPEGAL); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_INFEQU expression_arithmetique{ printf("\t\t\tOperateur <=\n"); $$=g_node_new((gpointer)INFEGAL); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_IN TOK_CROG expression_arithmetique TOK_FINSTR expression_arithmetique TOK_CROD{ printf("\t\t\tOperateur dans\n"); $$=g_node_new((gpointer)DANSII); g_node_append($$,$1); g_node_append($$,$4); g_node_append($$,$6); } | expression_arithmetique TOK_IN TOK_CROD expression_arithmetique TOK_FINSTR expression_arithmetique TOK_CROD{ printf("\t\t\tOperateur dans\n"); $$=g_node_new((gpointer)DANSEI); g_node_append($$,$1); g_node_append($$,$4); g_node_append($$,$6); } | expression_arithmetique TOK_IN TOK_CROG expression_arithmetique TOK_FINSTR expression_arithmetique TOK_CROG{ printf("\t\t\tOperateur dans\n"); $$=g_node_new((gpointer)DANSIE); g_node_append($$,$1); g_node_append($$,$4); g_node_append($$,$6); } | expression_arithmetique TOK_IN TOK_CROD expression_arithmetique TOK_FINSTR expression_arithmetique TOK_CROG{ printf("\t\t\tOperateur dans\n"); $$=g_node_new((gpointer)DANSEE); g_node_append($$,$1); g_node_append($$,$4); g_node_append($$,$6); }; <:vspace> addition: expression_arithmetique TOK_PLUS expression_arithmetique{ printf("\t\t\tAddition\n"); $$=g_node_new((gpointer)ADDITION); g_node_append($$,$1); g_node_append($$,$3); }; <:vspace> soustraction: expression_arithmetique TOK_MOINS expression_arithmetique{ printf("\t\t\tSoustraction\n"); $$=g_node_new((gpointer)SOUSTRACTION); g_node_append($$,$1); g_node_append($$,$3); }; <:vspace> multiplication: expression_arithmetique TOK_MUL expression_arithmetique{ printf("\t\t\tMultiplication\n"); $$=g_node_new((gpointer)MULTIPLICATION); g_node_append($$,$1); g_node_append($$,$3); }; <:vspace> division: expression_arithmetique TOK_DIV expression_arithmetique{ printf("\t\t\tDivision\n"); $$=g_node_new((gpointer)DIVISION); g_node_append($$,$1); g_node_append($$,$3); }; <:vspace> %% <:vspace> /* Dans la fonction main on appelle bien la routine yyparse() qui sera genere par Bison. Cette routine appellera yylex() de notre analyseur lexical. */ <:vspace> int main(int argc, char** argv){ /* recuperation du nom de fichier d'entree (langage Simple) donne en parametre */ char* fichier_entree=strdup(argv[1]); /* ouverture du fichier en lecture dans le flux d'entree stdin */ stdin=fopen(fichier_entree,"r"); /* creation fichier de sortie (langage C) */ char* fichier_sortie=strdup(argv[1]); /* remplace l'extension par .c */ strcpy(rindex(fichier_sortie, '.'), ".c"); /* ouvre le fichier cree en ecriture */ fichier=fopen(fichier_sortie, "w"); /* Creation de la table de hachage */ table_variable=g_hash_table_new_full(g_str_hash,g_str_equal,free,free); printf("Debut de l'analyse syntaxique :\n"); debut_code(); yyparse(); fin_code(); printf("Fin de l'analyse !\n"); printf("Resultat :\n"); if(error_lexical){ printf("\t-- Echec : Certains lexemes ne font pas partie du lexique du langage ! --\n"); printf("\t-- Echec a l'analyse lexicale --\n"); } else{ printf("\t-- Succes a l'analyse lexicale ! --\n"); } if(error_syntaxical){ printf("\t-- Echec : Certaines phrases sont syntaxiquement incorrectes ! --\n"); printf("\t-- Echec a l'analyse syntaxique --\n"); } else{ printf("\t-- Succes a l'analyse syntaxique ! --\n"); if(error_semantical){ printf("\t-- Echec : Certaines phrases sont semantiquement incorrectes ! --\n"); printf("\t-- Echec a l'analyse semantique --\n"); } else{ printf("\t-- Succes a l'analyse semantique ! --\n"); } } /* Suppression du fichier genere si erreurs analyse */ if(error_lexical||error_syntaxical||error_semantical){ remove(fichier_sortie); printf("ECHEC GENERATION CODE !\n"); } else{ printf("Le fichier \"%s\" a ete genere !\n",fichier_sortie); } /* Fermeture des flux */ fclose(fichier); fclose(stdin); /* Liberation memoire */ free(fichier_entree); free(fichier_sortie); g_hash_table_destroy(table_variable); return EXIT_SUCCESS; } <:vspace> void yyerror(char *s) { fprintf(stderr, "Erreur de syntaxe a la ligne %d: %s\n", lineno, s); } (:sourcend:) On oublie pas d'ajouter les nouvelles sequences de code dans le fichier d'entête : ⚠ (:source lang=c header="simple.h" linestart=50 linenum:) #define CONDITION_SI 29 #define CONDITION_SI_SINON 30 #define SI 31 #define SINON 32 #define NEGATIF 33 (:sourcend:) Et bien sûr éditer le générateur de code : ⚠ (:source lang=c header="generation_code.c" linestart=169 linenum:) case NEGATIF: fprintf(fichier,"-"); genere_code(g_node_nth_child(ast,0)); break; case CONDITION_SI: genere_code(g_node_nth_child(ast,0)); fprintf(fichier,"\n"); break; case CONDITION_SI_SINON: genere_code(g_node_nth_child(ast,0)); genere_code(g_node_nth_child(ast,1)); break; case SI: fprintf(fichier,"\tif("); genere_code(g_node_nth_child(ast,0)); fprintf(fichier,"){\n"); genere_code(g_node_nth_child(ast,1)); fprintf(fichier,"\t}"); break; case SINON: fprintf(fichier,"else{\n"); genere_code(g_node_nth_child(ast,0)); fprintf(fichier,"\t}\n"); break; (:sourcend:) On compile et maintenant on teste !! Je vous propose ce programme de test : ⚠ (:source lang=text header="programme.simple" linenum:) entier = 8; si entier > 5 alors booleen = faux; afficher booleen; sinon afficher entier; ; afficher booleen; (:sourcend:) On compile le fichier généré : ⚠ (:source lang=bash:) gcc programme.c (:sourcend:) Et on obtient l'erreur suivante (en tout cas moi oui) : ⚠ (:source lang=text:) programme.c: In function ‘main’: programme.c:15:16: error: ‘booleen’ undeclared (first use in this function) printf("%s\n",booleen?"vrai":"faux"); ^ programme.c:15:16: note: each undeclared identifier is reported only once for each function it appears in (:sourcend:) Arf ! On a un gros problème sur la portée de nos variables en fait. Le langage Simple donne la possibilité de déclarer une variable dans un bloc d'instruction et de la réutiliser à l'extérieur. A la ligne 3 du programme, j'ai déclaré une variable booleenne que j'initialise à faux et je réutilise cette même variable en ligne 8 à l'extérieur du "si". En C, cela n'est pas possible. Il faut donc adapter l'analyse sémantique pour prévenir l'erreur au développeur. Rappelez-vous, on stocke toutes les variables dans une table de hachage. Lorsqu'on rencontre une variable, si elle n'existe pas dans la table, on l'ajoute et le générateur de code la déclare. Mais si elle existe déjà, le générateur ne fait pas de déclaration. L'idée est de supprimer toutes les variables déclarées dans un bloc d'instruction, une fois sorti de ce bloc. On va ajouter une fonction qui supprime dans la table des variables toutes les variables nouvellement déclarées à l'intérieur d'un noeud d'arbre syntaxique. On va pour faire bien, déclarer un nouveau non terminal dans Bison, que l'on va appeler bloc_code. ⚠ (:source lang=c header="syntaxe_simple.y" linenum:) %{ <:vspace> #include "simple.h" bool error_syntaxical=false; bool error_semantical=false; /* Notre table de hachage */ GHashTable* table_variable; <:vspace> /* Fonction de suppression des variables declarees a l'interieur d'un arbre syntaxique */ void supprime_variable(GNode*); <:vspace> /* Notre structure Variable qui a comme membre le type et un pointeur generique vers la valeur */ typedef struct Variable Variable; <:vspace> struct Variable{ char* type; GNode* value; }; <:vspace> %} <:vspace> /* L'union dans Bison est utilisee pour typer nos tokens ainsi que nos non terminaux. Ici nous avons declare une union avec trois types : nombre de type int, texte de type pointeur de char (char*) et noeud d'arbre syntaxique (AST) de type (GNode*) */ <:vspace> %union { long nombre; char* texte; GNode* noeud; } <:vspace> /* Nous avons ici les operateurs, ils sont definis par leur ordre de priorite. Si je definis par exemple la multiplication en premier et l'addition apres, le + l'emportera alors sur le * dans le langage. Les parenthese sont prioritaires avec %right */ <:vspace> %left TOK_PLUS TOK_MOINS /* +- */ %left TOK_MUL TOK_DIV /* /* */ %left TOK_ET TOK_OU TOK_NON /* et ou non */ %left TOK_EQU TOK_DIFF TOK_SUP TOK_INF TOK_SUPEQU TOK_INFEQU /* comparaisons */ %right TOK_PARG TOK_PARD /* () */ <:vspace> /* Nous avons la liste de nos expressions (les non terminaux). Nous les typons tous en noeud de l'arbre syntaxique (GNode*) */ <:vspace> %type<noeud> code %type<noeud> bloc_code %type<noeud> instruction %type<noeud> condition %type<noeud> condition_si %type<noeud> condition_sinon %type<noeud> variable_arithmetique %type<noeud> variable_booleenne %type<noeud> affectation %type<noeud> affichage %type<noeud> expression_arithmetique %type<noeud> expression_booleenne %type<noeud> addition %type<noeud> soustraction %type<noeud> multiplication %type<noeud> division <:vspace> /* Nous avons la liste de nos tokens (les terminaux de notre grammaire) */ <:vspace> %token<nombre> TOK_NOMBRE %token TOK_VRAI /* true */ %token TOK_FAUX /* false */ %token TOK_AFFECT /* = */ %token TOK_FINSTR /* ; */ %token TOK_IN /* dans */ %token TOK_CROG TOK_CROD /* [] */ %token TOK_AFFICHER /* afficher */ %token<texte> TOK_VARB /* variable booleenne */ %token<texte> TOK_VARE /* variable arithmetique */ %token TOK_SI /* si */ %token TOK_ALORS /* alors */ %token TOK_SINON /* sinon */ <:vspace> %% <:vspace> /* Nous definissons toutes les regles grammaticales de chaque non terminal de notre langage. Par defaut on commence a definir l'axiome, c'est a dire ici le non terminal code. Si nous le definissons pas en premier nous devons le specifier en option dans Bison avec %start */ <:vspace> entree: code{ genere_code($1); g_node_destroy($1); }; <:vspace> code: %empty{$$=g_node_new((gpointer)CODE_VIDE);} | code instruction{ printf("Resultat : C'est une instruction valide !\n\n"); $$=g_node_new((gpointer)SEQUENCE); g_node_append($$,$1); g_node_append($$,$2); } | code error{ fprintf(stderr,"\tERREUR : Erreur de syntaxe a la ligne %d.\n",lineno); error_syntaxical=true; }; <:vspace> bloc_code: code{ $$=g_node_new((gpointer)BLOC_CODE); g_node_append($$,$1); /* on efface toutes les variables declarees de la table de hachage a la sortie du bloc de code */ supprime_variable($1); }; <:vspace> instruction: affectation{ printf("\tInstruction type Affectation\n"); $$=$1; } | affichage{ printf("\tInstruction type Affichage\n"); $$=$1; } | condition{ printf("Condition si/sinon\n"); $$=$1; }; <:vspace> variable_arithmetique: TOK_VARE{ printf("\t\t\tVariable entiere %s\n",$1); $$=g_node_new((gpointer)VARIABLE); g_node_append_data($$,strdup($1)); }; <:vspace> variable_booleenne: TOK_VARB{ printf("\t\t\tVariable booleenne %s\n",$1); $$=g_node_new((gpointer)VARIABLE); g_node_append_data($$,strdup($1)); }; <:vspace> condition: condition_si TOK_FINSTR{ printf("\tCondition si\n"); $$=g_node_new((gpointer)CONDITION_SI); g_node_append($$,$1); } | condition_si condition_sinon TOK_FINSTR{ printf("\tCondition si/sinon\n"); $$=g_node_new((gpointer)CONDITION_SI_SINON); g_node_append($$,$1); g_node_append($$,$2); }; <:vspace> condition_si: TOK_SI expression_booleenne TOK_ALORS bloc_code{ $$=g_node_new((gpointer)SI); g_node_append($$,$2); g_node_append($$,$4); }; <:vspace> condition_sinon: TOK_SINON bloc_code{ $$=g_node_new((gpointer)SINON); g_node_append($$,$2); }; <:vspace> affectation: variable_arithmetique TOK_AFFECT expression_arithmetique TOK_FINSTR{ /* $1 est la valeur du premier non terminal. Ici c'est la valeur du non terminal variable. $3 est la valeur du 2nd non terminal. */ printf("\t\tAffectation sur la variable\n"); Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($1,0)->data); if(var==NULL){ /* On cree une Variable et on lui affecte le type que nous connaissons et la valeur */ var=malloc(sizeof(Variable)); if(var!=NULL){ var->type=strdup("entier"); var->value=$3; /* On l'insere dans la table de hachage (cle: <nom_variable> / valeur: <(type,valeur)>) */ if(g_hash_table_insert(table_variable,g_node_nth_child($1,0)->data,var)){ $$=g_node_new((gpointer)AFFECTATIONE); g_node_append($$,$1); g_node_append($$,$3); }else{ fprintf(stderr,"ERREUR - PROBLEME CREATION VARIABLE !\n"); exit(-1); } }else{ fprintf(stderr,"ERREUR - PROBLEME ALLOCATION MEMOIRE VARIABLE !\n"); exit(-1); } }else{ $$=g_node_new((gpointer)AFFECTATION); g_node_append($$,$1); g_node_append($$,$3); } } | variable_booleenne TOK_AFFECT expression_booleenne TOK_FINSTR{ /* $1 est la valeur du premier non terminal. Ici c'est la valeur du non terminal variable. $3 est la valeur du 2nd non terminal. */ printf("\t\tAffectation sur la variable\n"); Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($1,0)->data); if(var==NULL){ /* On cree une Variable et on lui affecte le type que nous connaissons et la valeur */ var=malloc(sizeof(Variable)); if(var!=NULL){ var->type=strdup("booleen"); var->value=$3; /* On l'insere dans la table de hachage (cle: <nom_variable> / valeur: <(type,valeur)>) */ if(g_hash_table_insert(table_variable,g_node_nth_child($1,0)->data,var)){ $$=g_node_new((gpointer)AFFECTATIONB); g_node_append($$,$1); g_node_append($$,$3); }else{ fprintf(stderr,"ERREUR - PROBLEME CREATION VARIABLE !\n"); exit(-1); } }else{ fprintf(stderr,"ERREUR - PROBLEME ALLOCATION MEMOIRE VARIABLE !\n"); exit(-1); } }else{ $$=g_node_new((gpointer)AFFECTATION); g_node_append($$,$1); g_node_append($$,$3); } }; <:vspace> affichage: TOK_AFFICHER expression_arithmetique TOK_FINSTR{ printf("\t\tAffichage de la valeur de l'expression arithmetique\n"); $$=g_node_new((gpointer)AFFICHAGEE); g_node_append($$,$2); } | TOK_AFFICHER expression_booleenne TOK_FINSTR{ printf("\t\tAffichage de la valeur de l'expression booleenne\n"); $$=g_node_new((gpointer)AFFICHAGEB); g_node_append($$,$2); }; <:vspace> <:vspace> expression_arithmetique: TOK_NOMBRE{ printf("\t\t\tNombre : %ld\n",$1); /* Comme le token TOK_NOMBRE est de type entier et que on a type expression_arithmetique comme du texte, il nous faut convertir la valeur en texte. */ int length=snprintf(NULL,0,"%ld",$1); char* str=malloc(length+1); snprintf(str,length+1,"%ld",$1); $$=g_node_new((gpointer)ENTIER); g_node_append_data($$,strdup(str)); free(str); } | variable_arithmetique{ /* On recupere un pointeur vers la structure Variable */ Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($1,0)->data); /* Si on a trouve un pointeur valable */ if(var!=NULL){ /* On verifie que le type est bien un entier - Inutile car impose a l'analyse syntaxique */ if(strcmp(var->type,"entier")==0){ $$=$1; }else{ fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Type incompatible (entier attendu - valeur : %s) !\n",lineno,(char*)g_node_nth_child($1,0)->data); error_semantical=true; } /* Sinon on conclue que la variable n'a jamais ete declaree car absente de la table */ }else{ fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Variable %s jamais declaree !\n",lineno,(char*)g_node_nth_child($1,0)->data); error_semantical=true; } } | addition{ $$=$1; } | soustraction{ $$=$1; } | multiplication{ $$=$1; } | division{ $$=$1; } | TOK_PLUS expression_arithmetique{ $$=$2; } | TOK_MOINS expression_arithmetique{ printf("\t\t\tOperation unaire negation\n"); $$=g_node_new((gpointer)NEGATIF); g_node_append($$,$2); } | TOK_PARG expression_arithmetique TOK_PARD{ printf("\t\t\tC'est une expression artihmetique entre parentheses\n"); $$=g_node_new((gpointer)EXPR_PAR); g_node_append($$,$2); }; <:vspace> expression_booleenne: TOK_VRAI{ printf("\t\t\tBooleen Vrai\n"); $$=g_node_new((gpointer)VRAI); } | TOK_FAUX{ printf("\t\t\tBooleen Faux\n"); $$=g_node_new((gpointer)FAUX); } | variable_booleenne{ /* On recupere un pointeur vers la structure Variable */ Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($1,0)->data); /* Si on a trouve un pointeur valable */ if(var!=NULL){ /* On verifie que le type est bien un entier - Inutile car impose a l'analyse syntaxique */ if(strcmp(var->type,"booleen")==0){ $$=$1; }else{ fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Type incompatible (booleen attendu - valeur : %s) !\n",lineno,(char*)g_node_nth_child($1,0)->data); error_semantical=true; } /* Sinon on conclue que la variable n'a jamais ete declaree car absente de la table */ }else{ fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Variable %s jamais declaree !\n",lineno,(char*)g_node_nth_child($1,0)->data); error_semantical=true; } } | TOK_NON expression_booleenne{ printf("\t\t\tOperation booleenne Non\n"); $$=g_node_new((gpointer)NON); g_node_append($$,$2); } | expression_booleenne TOK_ET expression_booleenne{ printf("\t\t\tOperation booleenne Et\n"); $$=g_node_new((gpointer)ET); g_node_append($$,$1); g_node_append($$,$3); } | expression_booleenne TOK_OU expression_booleenne{ printf("\t\t\tOperation booleenne Ou\n"); $$=g_node_new((gpointer)OU); g_node_append($$,$1); g_node_append($$,$3); } | TOK_PARG expression_booleenne TOK_PARD{ printf("\t\t\tC'est une expression booleenne entre parentheses\n"); $$=g_node_new((gpointer)EXPR_PAR); g_node_append($$,$2); } | expression_booleenne TOK_EQU expression_booleenne{ printf("\t\t\tOperateur d'egalite ==\n"); $$=g_node_new((gpointer)EGALITE); g_node_append($$,$1); g_node_append($$,$3); } | expression_booleenne TOK_DIFF expression_booleenne{ printf("\t\t\tOperateur d'inegalite !=\n"); $$=g_node_new((gpointer)DIFFERENT); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_EQU expression_arithmetique{ printf("\t\t\tOperateur d'egalite ==\n"); $$=g_node_new((gpointer)EGALITE); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_DIFF expression_arithmetique{ printf("\t\t\tOperateur d'inegalite !=\n"); $$=g_node_new((gpointer)DIFFERENT); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_SUP expression_arithmetique{ printf("\t\t\tOperateur de superiorite >\n"); $$=g_node_new((gpointer)SUPERIEUR); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_INF expression_arithmetique{ printf("\t\t\tOperateur d'inferiorite <\n"); $$=g_node_new((gpointer)INFERIEUR); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_SUPEQU expression_arithmetique{ printf("\t\t\tOperateur >=\n"); $$=g_node_new((gpointer)SUPEGAL); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_INFEQU expression_arithmetique{ printf("\t\t\tOperateur <=\n"); $$=g_node_new((gpointer)INFEGAL); g_node_append($$,$1); g_node_append($$,$3); } | expression_arithmetique TOK_IN TOK_CROG expression_arithmetique TOK_FINSTR expression_arithmetique TOK_CROD{ printf("\t\t\tOperateur dans\n"); $$=g_node_new((gpointer)DANSII); g_node_append($$,$1); g_node_append($$,$4); g_node_append($$,$6); } | expression_arithmetique TOK_IN TOK_CROD expression_arithmetique TOK_FINSTR expression_arithmetique TOK_CROD{ printf("\t\t\tOperateur dans\n"); $$=g_node_new((gpointer)DANSEI); g_node_append($$,$1); g_node_append($$,$4); g_node_append($$,$6); } | expression_arithmetique TOK_IN TOK_CROG expression_arithmetique TOK_FINSTR expression_arithmetique TOK_CROG{ printf("\t\t\tOperateur dans\n"); $$=g_node_new((gpointer)DANSIE); g_node_append($$,$1); g_node_append($$,$4); g_node_append($$,$6); } | expression_arithmetique TOK_IN TOK_CROD expression_arithmetique TOK_FINSTR expression_arithmetique TOK_CROG{ printf("\t\t\tOperateur dans\n"); $$=g_node_new((gpointer)DANSEE); g_node_append($$,$1); g_node_append($$,$4); g_node_append($$,$6); }; <:vspace> addition: expression_arithmetique TOK_PLUS expression_arithmetique{ printf("\t\t\tAddition\n"); $$=g_node_new((gpointer)ADDITION); g_node_append($$,$1); g_node_append($$,$3); }; <:vspace> soustraction: expression_arithmetique TOK_MOINS expression_arithmetique{ printf("\t\t\tSoustraction\n"); $$=g_node_new((gpointer)SOUSTRACTION); g_node_append($$,$1); g_node_append($$,$3); }; <:vspace> multiplication: expression_arithmetique TOK_MUL expression_arithmetique{ printf("\t\t\tMultiplication\n"); $$=g_node_new((gpointer)MULTIPLICATION); g_node_append($$,$1); g_node_append($$,$3); }; <:vspace> division: expression_arithmetique TOK_DIV expression_arithmetique{ printf("\t\t\tDivision\n"); $$=g_node_new((gpointer)DIVISION); g_node_append($$,$1); g_node_append($$,$3); }; <:vspace> %% <:vspace> /* Dans la fonction main on appelle bien la routine yyparse() qui sera genere par Bison. Cette routine appellera yylex() de notre analyseur lexical. */ <:vspace> int main(int argc, char** argv){ /* recuperation du nom de fichier d'entree (langage Simple) donne en parametre */ char* fichier_entree=strdup(argv[1]); /* ouverture du fichier en lecture dans le flux d'entree stdin */ stdin=fopen(fichier_entree,"r"); /* creation fichier de sortie (langage C) */ char* fichier_sortie=strdup(argv[1]); /* remplace l'extension par .c */ strcpy(rindex(fichier_sortie, '.'), ".c"); /* ouvre le fichier cree en ecriture */ fichier=fopen(fichier_sortie, "w"); /* Creation de la table de hachage */ table_variable=g_hash_table_new_full(g_str_hash,g_str_equal,free,free); printf("Debut de l'analyse syntaxique :\n"); debut_code(); yyparse(); fin_code(); printf("Fin de l'analyse !\n"); printf("Resultat :\n"); if(error_lexical){ printf("\t-- Echec : Certains lexemes ne font pas partie du lexique du langage ! --\n"); printf("\t-- Echec a l'analyse lexicale --\n"); } else{ printf("\t-- Succes a l'analyse lexicale ! --\n"); } if(error_syntaxical){ printf("\t-- Echec : Certaines phrases sont syntaxiquement incorrectes ! --\n"); printf("\t-- Echec a l'analyse syntaxique --\n"); } else{ printf("\t-- Succes a l'analyse syntaxique ! --\n"); if(error_semantical){ printf("\t-- Echec : Certaines phrases sont semantiquement incorrectes ! --\n"); printf("\t-- Echec a l'analyse semantique --\n"); } else{ printf("\t-- Succes a l'analyse semantique ! --\n"); } } /* Suppression du fichier genere si erreurs analyse */ if(error_lexical||error_syntaxical||error_semantical){ remove(fichier_sortie); printf("ECHEC GENERATION CODE !\n"); } else{ printf("Le fichier \"%s\" a ete genere !\n",fichier_sortie); } /* Fermeture des flux */ fclose(fichier); fclose(stdin); /* Liberation memoire */ free(fichier_entree); free(fichier_sortie); g_hash_table_destroy(table_variable); return EXIT_SUCCESS; } <:vspace> void yyerror(char *s) { fprintf(stderr, "Erreur de syntaxe a la ligne %d: %s\n", lineno, s); } <:vspace> /* Cette fonction supprime dans la table de hachage toutes les variables declarees pour la premiere fois dans l'arbre syntaxique donne en parametre */ <:vspace> void supprime_variable(GNode* ast){ /* si l'element est n'est pas NULL et que ce n'est pas une feuille et que ce n'est pas un noeud d'arbre type bloc_code (pour eviter de supprimer des variables deja supprimees dans le cas ou on aurait des blocs de code dans des blocs de code */*/ if(ast&&!G_NODE_IS_LEAF(ast)&&(long)ast->data!=BLOC_CODE){ /* si le noeud est de type declaration */ if((long)ast->data==AFFECTATIONB||(long)ast->data==AFFECTATIONE){ /* suppression de la variable dans la table de hachage */ if(g_hash_table_remove(table_variable,(char*)g_node_nth_child(g_node_nth_child(ast,0),0)->data)){ printf("Variable supprimee !\n"); }else{ fprintf(stderr,"ERREUR - PROBLEME DE SUPPRESSION VARIABLE !\n"); exit(-1); } /* sinon on continue de parcourir l'arbre */ }else{ int nb_enfant; for(nb_enfant=0;nb_enfant<=g_node_n_children(ast);nb_enfant++){ supprime_variable(g_node_nth_child(ast,nb_enfant)); } } } } (:sourcend:) On réedite le fichier d'entête et le générateur de code : ⚠ (:source lang=c header="simple.h" linestart=55 linenum:) #define BLOC_CODE 34 (:sourcend:) ⚠ (:source lang=c header="generation_code.c" linestart=193 linenum:) case BLOC_CODE: genere_code(g_node_nth_child(ast,0)); break; (:sourcend:) Si on recompile tout et qu'on reteste avec le même programme, on a la log suivante : ⚠ (:source lang=text:) Debut de l'analyse syntaxique : Variable entiere entier Nombre : 8 Affectation sur la variable Instruction type Affectation Resultat : C'est une instruction valide ! <:vspace> Variable entiere entier Nombre : 5 Operateur de superiorite > Variable booleenne booleen Booleen Faux Affectation sur la variable Instruction type Affectation Resultat : C'est une instruction valide ! <:vspace> Variable booleenne booleen Affichage de la valeur de l'expression booleenne Instruction type Affichage Resultat : C'est une instruction valide ! <:vspace> Variable booleen supprimee ! Variable entiere entier Affichage de la valeur de l'expression arithmetique Instruction type Affichage Resultat : C'est une instruction valide ! <:vspace> Condition si/sinon Condition si/sinon Resultat : C'est une instruction valide ! <:vspace> Variable booleenne booleen ERREUR : Erreur de semantique a la ligne 8. Variable booleen jamais declaree ! Affichage de la valeur de l'expression booleenne Instruction type Affichage Resultat : C'est une instruction valide ! <:vspace> Fin de l'analyse ! Resultat : -- Succes a l'analyse lexicale ! -- -- Succes a l'analyse syntaxique ! -- -- Echec : Certaines phrases sont semantiquement incorrectes ! -- -- Echec a l'analyse semantique -- ECHEC GENERATION CODE ! (:sourcend:) Il est dit que la variable booleen a été supprimée tout de suite à la sortie du "si". Ce qui a provoqué fatalement une erreur sémantique quand on la réutilise après. Changeons donc le programme pour que cela puisse correctement passer à la compilation : ⚠ (:source lang=text header="programme.simple" linenum:) entier = 8; si entier > 5 alors booleen = faux; afficher booleen; sinon afficher entier; ; booleen = vrai; afficher booleen; (:sourcend:) La compilation se passe cette fois-ci bien. ⚠ (:source lang=bash:) gcc -o programme programme.c ./programme (:sourcend:) ⚠ (:source lang=text:) faux vrai (:sourcend:) La prochaine évolution sera consacrée aux commentaires. A l'heure actuelle, il nous est impossible de commenter notre code. Les commentaires dans ce langage passent par l'analyseur syntaxique. Il faut dire à ce dernier qu'il s'agit de commentaires et qu'il doit les ignorer.

<< Évolution 1 : Opérateurs de comparaison | Évolution 2 : If/Else | Évolution 3 : Les commentaires >>

Thomas - (CC BY-NC-SA 3.0 FR)

Edit - History - Print - Recent Changes - Search
Page last modified on July 09, 2017, at 07:49 PM EST