Recent Changes - Search:

My Projects

Courses

Writings

Source Code

Social Networks

Live Traffic !

Analyse sémantique

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

<< Analyseur syntaxique | Analyse sémantique | Arbre syntaxique et Génération de code >>

Nous allons utiliser une structure de données pour vérifier que les variables ont bien été déclarées et qu'elles possèdent une valeur pour qu'elles soient utilisées dans des expressions. On vérifiera aussi le typage des variables. Bien que l'analyse syntaxique le fait, j'ai quand même trouvé utile de faire la vérification à l'analyse sémantique. Mon code vous permettra facilement de faire évoluer le langage Simple si un jour vous ne voulez plus être imposé par le renommage des variables. La structure de données adoptée, car idéale me semble-t-il, est la table de hachage. Nous allons utiliser pour cela la librairie GLib. Cela nous assure une certaine fiabilité et surtout nous évite de coder une table de hachage. La table aura pour clés les noms des variables créées et comme valeur associée une structure que j'ai créée. La structure a comme membre le nom du type de la variable (char*) et un pointeur générique vers la valeur de la variable (void*). Il n'existe pas de générateur d'analyseur sémantique et nous ne touchons pas à l'analyseur lexical. Les tests de sémantique se feront dans le même temps que l'analyse syntaxique. Nous allons donc modifier notre code Bison.

Voici le joli code :

syntaxe_simple.y
  1. %{
  2.  
  3. #include "simple.h"
  4. #include <string.h>
  5. #include <glib.h>
  6. bool error_syntaxical=false;
  7. bool error_semantical=false;
  8. extern unsigned int lineno;
  9. extern bool error_lexical;
  10. /* Notre table de hachage */
  11. GHashTable* table_variable;
  12.  
  13. /* Notre structure Variable qui a comme membre le type et un pointeur generique vers la valeur */
  14. typedef struct Variable Variable;
  15.  
  16. struct Variable{
  17.         char* type;
  18.         void* value;
  19. };
  20.  
  21. %}
  22.  
  23. /* L'union dans Bison est utilisee pour typer nos tokens ainsi que nos non terminaux. Ici nous avons declare une union avec deux types : nombre de type int et texte de type pointeur de char (char*) */
  24.  
  25. %union {
  26.         long nombre;
  27.         char* texte;
  28. }
  29.  
  30. /* 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 */
  31.  
  32. %left                   TOK_PLUS        TOK_MOINS       /* +- */
  33. %left                   TOK_MUL         TOK_DIV         /* /* */
  34. %left                   TOK_ET          TOK_OU          TOK_NON         /* et ou non */
  35. %right                  TOK_PARG        TOK_PARD        /* () */
  36.  
  37. /* Nous avons la liste de nos expressions (les non terminaux). Nous les typons tous en texte (pointeur vers une zone de char). */
  38.  
  39. %type<texte>            code
  40. %type<texte>            instruction
  41. %type<texte>            variable_arithmetique
  42. %type<texte>            variable_booleenne
  43. %type<texte>            affectation
  44. %type<texte>            affichage
  45. %type<texte>            expression_arithmetique
  46. %type<texte>            expression_booleenne
  47. %type<texte>            addition
  48. %type<texte>            soustraction
  49. %type<texte>            multiplication
  50. %type<texte>            division
  51.  
  52. /* Nous avons la liste de nos tokens (les terminaux de notre grammaire) */
  53.  
  54. %token<nombre>          TOK_NOMBRE
  55. %token                  TOK_VRAI        /* true */
  56. %token                  TOK_FAUX        /* false */
  57. %token                  TOK_AFFECT      /* = */
  58. %token                  TOK_FINSTR      /* ; */
  59. %token                  TOK_AFFICHER    /* afficher */
  60. %token<texte>           TOK_VARB        /* variable booleenne */
  61. %token<texte>           TOK_VARE        /* variable arithmetique */
  62.  
  63. %%
  64.  
  65. /* 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 */
  66.  
  67. code:           %empty{}
  68.                 |
  69.                 code instruction{
  70.                         printf("Resultat : C'est une instruction valide !\n\n");
  71.                 }
  72.                 |
  73.                 code error{
  74.                         fprintf(stderr,"\tERREUR : Erreur de syntaxe a la ligne %d.\n",lineno);
  75.                         error_syntaxical=true;
  76.                 };
  77.  
  78. instruction:    affectation{
  79.                         printf("\tInstruction type Affectation\n");
  80.                 }
  81.                 |
  82.                 affichage{
  83.                          printf("\tInstruction type Affichage\n");
  84.                 };
  85.  
  86. variable_arithmetique:  TOK_VARE{
  87.                                 printf("\t\t\tVariable entiere %s\n",$1);
  88.                                 $$=strdup($1);
  89.                         };
  90.  
  91. variable_booleenne:     TOK_VARB{
  92.                                 printf("\t\t\tVariable booleenne %s\n",$1);
  93.                                 $$=strdup($1);
  94.                         };
  95.  
  96. affectation:    variable_arithmetique TOK_AFFECT expression_arithmetique TOK_FINSTR{
  97.                         /* $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. */
  98.                         printf("\t\tAffectation sur la variable %s\n",$1);
  99.                         /* On cree une Variable et on lui affecte le type que nous connaissons et la valeur */
  100.                         Variable* var=malloc(sizeof(Variable));
  101.                         if(var!=NULL){
  102.                                 var->type=strdup("entier");
  103.                                 var->value=strdup($3);
  104.                                 /* On l'insere dans la table de hachage (cle: <nom_variable> / valeur: <(type,valeur)>) */
  105.                                 if(!g_hash_table_insert(table_variable,strdup($1),var)){
  106.                                         fprintf(stderr,"ERREUR - PROBLEME CREATION VARIABLE !\n");
  107.                                         exit(-1);
  108.                                 }
  109.                         }else{
  110.                                 fprintf(stderr,"ERREUR - PROBLEME ALLOCATION MEMOIRE VARIABLE !\n");
  111.                                 exit(-1);
  112.                         }
  113.                 }
  114.                 |
  115.                 variable_booleenne TOK_AFFECT expression_booleenne TOK_FINSTR{
  116.                         printf("\t\tAffectation sur la variable %s\n",$1);
  117.                         Variable* var=malloc(sizeof(Variable));
  118.                         if(var!=NULL){
  119.                                 var->type=strdup("booleen");
  120.                                 var->value=strdup($3);
  121.                                 if(!g_hash_table_insert(table_variable,strdup($1),var)){
  122.                                         fprintf(stderr,"ERREUR - PROBLEME CREATION VARIABLE !\n");
  123.                                         exit(-1);
  124.                                 }
  125.                         }else{
  126.                                 fprintf(stderr,"ERREUR - PROBLEME ALLOCATION MEMOIRE VARIABLE !\n");
  127.                                 exit(-1);
  128.                         }
  129.                 };
  130.  
  131. affichage:      TOK_AFFICHER expression_arithmetique TOK_FINSTR{
  132.                         printf("\t\tAffichage de la valeur de l'expression arithmetique %s\n",$2);
  133.                 }
  134.                 |
  135.                 TOK_AFFICHER expression_booleenne TOK_FINSTR{
  136.                         printf("\t\tAffichage de la valeur de l'expression booleenne %s\n",$2);
  137.                 };
  138.  
  139. expression_arithmetique:        TOK_NOMBRE{
  140.                                         printf("\t\t\tNombre : %ld\n",$1);
  141.                                         /* 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. */
  142.                                         int length=snprintf(NULL,0,"%ld",$1);
  143.                                         char* str=malloc(length+1);
  144.                                         snprintf(str,length+1,"%ld",$1);
  145.                                         $$=strdup(str);
  146.                                         free(str);
  147.                                 }
  148.                                 |
  149.                                 variable_arithmetique{
  150.                                         /* On recupere un pointeur vers la structure Variable */
  151.                                         Variable* var=g_hash_table_lookup(table_variable,$1);
  152.                                         /* Si on a trouve un pointeur valable */
  153.                                         if(var!=NULL){
  154.                                                 /* On verifie que le type est bien un entier - Inutile car impose a l'analyse syntaxique */
  155.                                                 if(strcmp(var->type,"entier")==0){
  156.                                                         $$=strdup($1);
  157.                                                 }else{
  158.                                                         fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Type incompatible (entier attendu - valeur : %s) !\n",lineno,$1,(char*)var->value);
  159.                                                         error_semantical=true;
  160.                                                 }
  161.                                         /* Sinon on conclue que la variable n'a jamais ete declaree car absente de la table */
  162.                                         }else{
  163.                                                 fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Variable %s jamais declaree !\n",lineno,$1);
  164.                                                 error_semantical=true;
  165.                                         }
  166.                                 }
  167.                                 |
  168.                                 addition{
  169.                                 }
  170.                                 |
  171.                                 soustraction{
  172.                                 }
  173.                                 |
  174.                                 multiplication{
  175.                                 }
  176.                                 |
  177.                                 division{
  178.                                 }
  179.                                 |
  180.                                 TOK_PARG expression_arithmetique TOK_PARD{
  181.                                         printf("\t\t\tC'est une expression artihmetique entre parentheses\n");
  182.                                         $$=strcat(strcat(strdup("("),strdup($2)),strdup(")"));
  183.                                 };
  184.  
  185. expression_booleenne:           TOK_VRAI{
  186.                                         printf("\t\t\tBooleen Vrai\n");
  187.                                         $$=strdup("vrai");
  188.                                 }
  189.                                 |
  190.                                 TOK_FAUX{
  191.                                         printf("\t\t\tBooleen Faux\n");
  192.                                         $$=strdup("faux");
  193.                                 }
  194.                                 |
  195.                                 variable_booleenne{
  196.                                         /* On recupere un pointeur vers la structure Variable */
  197.                                         Variable* var=g_hash_table_lookup(table_variable,$1);
  198.                                         /* Si on a trouve un pointeur valable */
  199.                                         if(var!=NULL){
  200.                                                 /* On verifie que le type est bien un entier - Inutile car impose a l'analyse syntaxique */
  201.                                                 if(strcmp(var->type,"booleen")==0){
  202.                                                         $$=strdup($1);
  203.                                                 }else{
  204.                                                         fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Type incompatible (booleen attendu - valeur : %s) !\n",lineno,$1,(char*)var->value);
  205.                                                         error_semantical=true;
  206.                                                 }
  207.                                         /* Sinon on conclue que la variable n'a jamais ete declaree car absente de la table */
  208.                                         }else{
  209.                                                 fprintf(stderr,"\tERREUR : Erreur de semantique a la ligne %d. Variable %s jamais declaree !\n",lineno,$1);
  210.                                                 error_semantical=true;
  211.                                         }
  212.                                 }
  213.                                 |
  214.                                 TOK_NON expression_booleenne{
  215.                                         printf("\t\t\tOperation booleenne Non\n");
  216.                                         $$=strcat(strdup("non "), strndup($2,sizeof(char)*strlen($2)));
  217.                                 }
  218.                                 |
  219.                                 expression_booleenne TOK_ET expression_booleenne{
  220.                                         printf("\t\t\tOperation booleenne Et\n");
  221.                                         $$=strcat(strcat(strdup($1),strdup(" et ")),strdup($3));
  222.                                 }
  223.                                 |
  224.                                 expression_booleenne TOK_OU expression_booleenne{
  225.                                         printf("\t\t\tOperation booleenne Ou\n");
  226.                                         $$=strcat(strcat(strdup($1),strdup(" ou ")),strdup($3));
  227.                                 }
  228.                                 |
  229.                                 TOK_PARG expression_booleenne TOK_PARD{
  230.                                         printf("\t\t\tC'est une expression booleenne entre parentheses\n");
  231.                                         $$=strcat(strcat(strdup("("),strdup($2)),strdup(")"));
  232.                                 };
  233.  
  234. addition:       expression_arithmetique TOK_PLUS expression_arithmetique{printf("\t\t\tAddition\n");$$=strcat(strcat(strdup($1),strdup("+")),strdup($3));};
  235. soustraction:   expression_arithmetique TOK_MOINS expression_arithmetique{printf("\t\t\tSoustraction\n");$$=strcat(strcat(strdup($1),strdup("-")),strdup($3));};
  236. multiplication: expression_arithmetique TOK_MUL expression_arithmetique{printf("\t\t\tMultiplication\n");$$=strcat(strcat(strdup($1),strdup("*")),strdup($3));};
  237. division:       expression_arithmetique TOK_DIV expression_arithmetique{printf("\t\t\tDivision\n");$$=strcat(strcat(strdup($1),strdup("/")),strdup($3));};
  238.  
  239. %%
  240.  
  241. /* Dans la fonction main on appelle bien la routine yyparse() qui sera genere par Bison. Cette routine appellera yylex() de notre analyseur lexical. */
  242.  
  243. int main(void){
  244.         /* Creation de la table de hachage */
  245.         table_variable=g_hash_table_new_free(g_str_hash,g_str_equal,free,free);
  246.         printf("Debut de l'analyse syntaxique :\n");
  247.         yyparse();
  248.         printf("Fin de l'analyse !\n");
  249.         printf("Resultat :\n");
  250.         if(error_lexical){
  251.                 printf("\t-- Echec : Certains lexemes ne font pas partie du lexique du langage ! --\n");
  252.                 printf("\t-- Echec a l'analyse lexicale --\n");
  253.         }
  254.         else{
  255.                 printf("\t-- Succes a l'analyse lexicale ! --\n");
  256.         }
  257.         if(error_syntaxical){
  258.                 printf("\t-- Echec : Certaines phrases sont syntaxiquement incorrectes ! --\n");
  259.                 printf("\t-- Echec a l'analyse syntaxique --\n");
  260.         }
  261.         else{
  262.                 printf("\t-- Succes a l'analyse syntaxique ! --\n");
  263.                 if(error_semantical){
  264.                         printf("\t-- Echec : Certaines phrases sont semantiquement incorrectes ! --\n");
  265.                         printf("\t-- Echec a l'analyse semantique --\n");
  266.                 }
  267.                 else{
  268.                         printf("\t-- Succes a l'analyse semantique ! --\n");
  269.                 }
  270.         }
  271.         /* Liberation memoire : suppression de la table */
  272.         g_hash_table_destroy(table_variable);
  273.         return EXIT_SUCCESS;
  274. }
  275. void yyerror(char *s) {
  276.         fprintf(stderr, "Erreur de syntaxe a la ligne %d: %s\n", lineno, s);
  277. }

Compilons tout les fichiers :

flex -o lexique_simple.c lexique_simple.lex
bison -d syntaxe_simple.y
gcc lexique_simple.c syntaxe_simple.tab.c `pkg-config --cflags --libs glib-2.0` -o simple

Testons avec un programme semantiquement incorrect avec des variables non declarées :

programme_faux.simple
  1. afficher (3 * entier)+4;
  2. entier = 2;
  3. booleen = booleen et vrai;
  4. booleen = vrai et non faux;
  5. afficher booleen;
  6. booleen = non booleen;
  7. afficher booleen;

Résultat :

Debut de l'analyse syntaxique :
                        Nombre : 3
                        Variable entiere entier
        ERREUR : Erreur de semantique a la ligne 1. Variable entier jamais declaree !
                        Multiplication
                        C'est une expression artihmetique entre parentheses
                        Nombre : 4
                        Addition
                Affichage de la valeur de l'expression (3*entier)+4
        Instruction type Affichage
Resultat : C'est une instruction valide !

                        Variable entiere entier
                        Nombre : 2
                Affectation sur la variable entier
        Instruction type Affectation
Resultat : C'est une instruction valide !

                        Variable booleenne booleen
                        Variable booleenne booleen
        ERREUR : Erreur de semantique a la ligne 3. Variable booleen jamais declaree !
                        Booleen Vrai
                        Operation booleenne Et
                Affectation sur la variable booleen
        Instruction type Affectation
Resultat : C'est une instruction valide !

                        Variable booleenne booleen
                        Booleen Vrai
                        Booleen Faux
                        Operation booleenne Non
                        Operation booleenne Et
                Affectation sur la variable booleen
        Instruction type Affectation
Resultat : C'est une instruction valide !

                        Variable booleenne booleen
                Affichage de la valeur de l'expression booleen
        Instruction type Affichage
Resultat : C'est une instruction valide !

                        Variable booleenne booleen
                        Variable booleenne booleen
                        Operation booleenne Non
                Affectation sur la variable booleen
        Instruction type Affectation
Resultat : C'est une instruction valide !

                        Variable booleenne booleen
                Affichage de la valeur de l'expression booleen
        Instruction type Affichage
Resultat : C'est une instruction valide !

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 --

L'analyse sémantique a bien été mise en échec. Reprenons le programme testé et remettons les instructions dans le bon ordre pour avoir un sens logique :

programme.simple
  1. entier = 2;
  2. afficher (3 * entier)+4;
  3. booleen = vrai et non faux;
  4. booleen = booleen et vrai;
  5. afficher booleen;
  6. booleen = non booleen;
  7. afficher booleen;

Résultat :

Debut de l'analyse syntaxique :
                        Variable entiere entier
                        Nombre : 2
                Affectation sur la variable entier
        Instruction type Affectation
Resultat : C'est une instruction valide !

                        Nombre : 3
                        Variable entiere entier
                        Multiplication
                        C'est une expression artihmetique entre parentheses
                        Nombre : 4
                        Addition
                Affichage de la valeur de l'expression (3*entier)+4
        Instruction type Affichage
Resultat : C'est une instruction valide !

                        Variable booleenne booleen
                        Booleen Vrai
                        Booleen Faux
                        Operation booleenne Non
                        Operation booleenne Et
                Affectation sur la variable booleen
        Instruction type Affectation
Resultat : C'est une instruction valide !

                        Variable booleenne booleen
                        Variable booleenne booleen
                        Booleen Vrai
                        Operation booleenne Et
                Affectation sur la variable booleen
        Instruction type Affectation
Resultat : C'est une instruction valide !

                        Variable booleenne booleen
                Affichage de la valeur de l'expression booleen
        Instruction type Affichage
Resultat : C'est une instruction valide !

                        Variable booleenne booleen
                        Variable booleenne booleen
                        Operation booleenne Non
                Affectation sur la variable booleen
        Instruction type Affectation
Resultat : C'est une instruction valide !

                        Variable booleenne booleen
                Affichage de la valeur de l'expression booleen
        Instruction type Affichage
Resultat : C'est une instruction valide !

Fin de l'analyse !
Resultat :
        -- Succes a l'analyse lexicale ! --
        -- Succes a l'analyse syntaxique ! --
        -- Succes a l'analyse semantique ! --

Maintenant que nous avons fait les 3 analyses, nous allons pouvoir passer à la dernière étape de la compilation : la génération du code. Je vous dis donc à tout de suite au prochain chapitre.

<< Analyseur syntaxique | Analyse sémantique | Arbre syntaxique et Génération de code >>

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

Page last modified on July 09, 2017, at 07:39 PM EST

This page has been requested 5002 times (Today : 5) - Total number of requests : 263635

Edit - History - Statistics - Print - Recent Changes - Search

Clin d'oeil aux victimes des attentats survenus dans la soirée du 13 novembre 2015. La nouvelle version du site a été installée quelques heures avant.