Créer son propre langage de programmation de A à Z
<< Évolution 9 : Les nombres décimaux | Évolution 10 : Les entrées clavier | Prochainement Évolution 11 >>
Nous allons mettre en place une nouvelle instruction Simple qui sera la saisie. Simple n'offre actuellement aucune interactivité avec l'utilisateur, il exécute bêtement un programme. Nous allons pallier à ce manque dans cette évolution. Nous allons pouvoir saisir au clavier, quand le programme nous le demande, une chaîne de caractère, un entier, un booléen ou encore un décimal (les 4 types natifs de Simple).
Évolution 10 du langage :
⚠ (:source lang=bnf linenum:)
<saisie> ::= "saisir" ( variable_decimale | variable_entiere | variable_texte | variable_booleenne ) ";"
<:vspace>
<instruction> ::= <instruction> | <saisie>
(:sourcend:)
On a un nouveau lexème "saisir" à faire connaître à l’analyseur lexical :
⚠ (:source lang=c header="lexique_simple.lex" linestart=180 linenum:)
"saisir" {return TOK_SAISIR;}
(:sourcend:)
Les possibilités de code dans le fichier d'entête :
⚠ (:source lang=c header="simple.h" linestart=83 linenum:)
#define SAISIEE 59
#define SAISIEB 60
#define SAISIED 61
#define SAISIET 62
#define SAISIENE 63
#define SAISIENB 64
#define SAISIEND 65
#define SAISIENT 66
(:sourcend:)
On a deux types de saisie pour chaque type natif de variable : Un pour affecter et un pour déclarer puis affecter si la variable n'a jamais été déclarée.
On met à jour l'analyseur syntaxique :
⚠ (:source lang=c header="syntaxe_simple.y" linestart=100 linenum:)
%token TOK_SAISIR /* saisir */
(:sourcend:)
⚠ (:source lang=c header="syntaxe_simple.y" linestart=141 linenum:)
instruction: affectation{
printf("\tInstruction type Affectation\n");
$$=$1;
}
|
affichage{
printf("\tInstruction type Affichage\n");
$$=$1;
}
|
condition{
printf("Condition si/sinon\n");
$$=$1;
}
|
boucle_for{
printf("Boucle repetee\n");
$$=$1;
}
|
boucle_while{
printf("Boucle tant que\n");
$$=$1;
}
|
boucle_do_while{
printf("Boucle faire tant que\n");
$$=$1;
}
|
suppression{
printf("\tInstruction type Suppression\n");
$$=$1;
}
|
saisie{
printf("\tInstruction type Saisie\n");
$$=$1;
};
(:sourcend:)
⚠ (:source lang=c header="syntaxe_simple.y" linestart=579 linenum:)
saisie: TOK_SAISIR variable_texte TOK_FINSTR{
Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($2,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("texte");
var->value=NULL;
/* 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($2,0)->data,var)){
$$=g_node_new((gpointer)SAISIENT);
g_node_append($$,$2);
}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)SAISIET);
g_node_append($$,$2);
}
}
|
TOK_SAISIR variable_entiere TOK_FINSTR{
Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($2,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=NULL;
/* 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($2,0)->data,var)){
$$=g_node_new((gpointer)SAISIENE);
g_node_append($$,$2);
}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)SAISIEE);
g_node_append($$,$2);
}
}
|
TOK_SAISIR variable_booleenne TOK_FINSTR{
Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($2,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=NULL;
/* 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($2,0)->data,var)){
$$=g_node_new((gpointer)SAISIENB);
g_node_append($$,$2);
}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)SAISIEB);
g_node_append($$,$2);
}
}
|
TOK_SAISIR variable_decimale TOK_FINSTR{
Variable* var=g_hash_table_lookup(table_variable,(char*)g_node_nth_child($2,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("decimal");
var->value=NULL;
/* 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($2,0)->data,var)){
$$=g_node_new((gpointer)SAISIEND);
g_node_append($$,$2);
}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)SAISIED);
g_node_append($$,$2);
}
};
(:sourcend:)
Bien, on passe au plus difficile maintenant : la génération de code. Il faut éviter les dépassements de tampon (buffer overflow en anglais) en C. Pour ce faire, on alloue une zone de char de manière dynamique car on ne connaît pas à l'avance la longueur de la chaîne qui sera saisie. On commence par allouer une zone de 1 char. Puis à la saisie d'un nouvel caractère par l'utilisateur, on demande la réallocation dynamique et d'agrandir la taille d'un char. Et ce jusqu'à ce que l'utilisateur finit sa saisie en appuyant sur la touche Entrée. Cela donne en C ce code :
⚠ (:source lang=c linenum:)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
<:vspace>
int main(int argc, char *argv[]) {
// on alloue une zone de char de 1 char
char* str=malloc(sizeof(char));
// caractere courant saisi
char c;
// nombre de caractere saisie
int i=0;
do{
// char lu
c=getchar();
str[i]=c;
// reallocation -> agrandit la taille de 1 char de plus
str=realloc(str,sizeof(char)*(i+2));
i++;
}while(c!='\n'); //jusqu'a ce que l'utilisateur appuie sur Entree
str[i-1]='\0'; //on tronque la chaine, on enleve le dernier caractere qui est le retour chariot '\n'
return EXIT_SUCCESS;
}
(:sourcend:)
Pour lire un double ou un entier, on va utiliser les fonctions C respectives strtod et strold qui caste la chaîne. Pour les booléens, si la chaîne saisie est égale à "vrai", "1" ou "true", alors ce sera vrai sinon faux. Voici le générateur de code :
⚠ (:source lang=c header="generation_code.c" linestart=381 linenum:)
case SAISIENT:
fprintf(fichier,"\tchar* ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,";\n\tif((");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"=malloc(");
fprintf(fichier,"sizeof(char)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie=0;\n");
fprintf(fichier,"\tdo{\n");
fprintf(fichier,"\t_csaisie=getchar();\n\t");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"[_esaisie]=_csaisie;\n");
fprintf(fichier,"\tif((");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"=realloc(");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,",sizeof(char)*(_esaisie+2)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie++;\n");
fprintf(fichier,"\t}while(_csaisie!='\\n');\n");
fprintf(fichier,"\t");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"[_esaisie-1]='\\0';\n");
break;
case SAISIET:
fprintf(fichier,"\tif((");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"=realloc(");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,",sizeof(char)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie=0;\n");
fprintf(fichier,"\tdo{\n");
fprintf(fichier,"\t_csaisie=getchar();\n\t");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"[_esaisie]=_csaisie;\n");
fprintf(fichier,"\tif((");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"=realloc(");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,",sizeof(char)*(_esaisie+2)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie++;\n");
fprintf(fichier,"\t}while(_csaisie!='\\n');\n");
fprintf(fichier,"\t");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"[_esaisie-1]='\\0';\n");
break;
case SAISIENE:
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=malloc(");
fprintf(fichier,"sizeof(char)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie=0;\n");
fprintf(fichier,"\tdo{\n");
fprintf(fichier,"\t_csaisie=getchar();\n");
fprintf(fichier,"\t_tsaisie[_esaisie]=_csaisie;\n");
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=realloc(_tsaisie");
fprintf(fichier,",sizeof(char)*(_esaisie+2)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie++;\n");
fprintf(fichier,"\t}while(_csaisie!='\\n');\n");
fprintf(fichier,"\t_tsaisie");
fprintf(fichier,"[_esaisie-1]='\\0';\n");
fprintf(fichier,"\tlong ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"=strtold(_tsaisie,NULL);\n\tfree(_tsaisie);\n");
break;
case SAISIEE:
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=malloc(");
fprintf(fichier,"sizeof(char)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie=0;\n");
fprintf(fichier,"\tdo{\n");
fprintf(fichier,"\t_csaisie=getchar();\n");
fprintf(fichier,"\t_tsaisie[_esaisie]=_csaisie;\n");
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=realloc(_tsaisie");
fprintf(fichier,",sizeof(char)*(_esaisie+2)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie++;\n");
fprintf(fichier,"\t}while(_csaisie!='\\n');\n");
fprintf(fichier,"\t_tsaisie[_esaisie-1]='\\0';\n\t");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"=strtold(_tsaisie,NULL);\n\tfree(_tsaisie);\n");
break;
case SAISIENB:
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=malloc(");
fprintf(fichier,"sizeof(char)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie=0;\n");
fprintf(fichier,"\tdo{\n");
fprintf(fichier,"\t_csaisie=getchar();\n");
fprintf(fichier,"\t_tsaisie[_esaisie]=_csaisie;\n");
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=realloc(_tsaisie");
fprintf(fichier,",sizeof(char)*(_esaisie+2)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie++;\n");
fprintf(fichier,"\t}while(_csaisie!='\\n');\n");
fprintf(fichier,"\t_tsaisie");
fprintf(fichier,"[_esaisie-1]='\\0';\n");
fprintf(fichier,"\tbool ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"=(strcmp(_tsaisie,\"vrai\")==0||strcmp(_tsaisie,\"true\")==0||strcmp(_tsaisie,\"1\")==0);\n\tfree(_tsaisie);\n");
break;
case SAISIEB:
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=malloc(");
fprintf(fichier,"sizeof(char)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie=0;\n");
fprintf(fichier,"\tdo{\n");
fprintf(fichier,"\t_csaisie=getchar();\n");
fprintf(fichier,"\t_tsaisie[_esaisie]=_csaisie;\n");
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=realloc(_tsaisie");
fprintf(fichier,",sizeof(char)*(_esaisie+2)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie++;\n");
fprintf(fichier,"\t}while(_csaisie!='\\n');\n");
fprintf(fichier,"\t_tsaisie");
fprintf(fichier,"[_esaisie-1]='\\0';\n\t");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"=(strcmp(_tsaisie,\"vrai\")==0||strcmp(_tsaisie,\"true\")==0||strcmp(_tsaisie,\"1\")==0);\n\tfree(_tsaisie);\n");
break;
case SAISIEND:
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=malloc(");
fprintf(fichier,"sizeof(char)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie=0;\n");
fprintf(fichier,"\tdo{\n");
fprintf(fichier,"\t_csaisie=getchar();\n");
fprintf(fichier,"\t_tsaisie[_esaisie]=_csaisie;\n");
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=realloc(_tsaisie");
fprintf(fichier,",sizeof(char)*(_esaisie+2)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie++;\n");
fprintf(fichier,"\t}while(_csaisie!='\\n');\n");
fprintf(fichier,"\t_tsaisie");
fprintf(fichier,"[_esaisie-1]='\\0';\n");
fprintf(fichier,"\tdouble ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"=strtod(_tsaisie,NULL);\n\tfree(_tsaisie);\n");
break;
case SAISIED:
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=malloc(");
fprintf(fichier,"sizeof(char)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie=0;\n");
fprintf(fichier,"\tdo{\n");
fprintf(fichier,"\t_csaisie=getchar();\n");
fprintf(fichier,"\t_tsaisie[_esaisie]=_csaisie;\n");
fprintf(fichier,"\tif((_tsaisie");
fprintf(fichier,"=realloc(_tsaisie");
fprintf(fichier,",sizeof(char)*(_esaisie+2)))==NULL){\n");
fprintf(fichier,"\tprintf(\"Erreur de reallocation memoire sur la variable ");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier," !\");\n\texit(-1);\n\t}\n");
fprintf(fichier,"\t_esaisie++;\n");
fprintf(fichier,"\t}while(_csaisie!='\\n');\n");
fprintf(fichier,"\t_tsaisie[_esaisie-1]='\\0';\n\t");
genere_code(g_node_nth_child(ast,0));
fprintf(fichier,"=strtod(_tsaisie,NULL);\n\tfree(_tsaisie);\n");
break;
(:sourcend:)
J'ai changé la fonction debut_code() du générateur pour déclarer les variables temporaires relatives à la saisie :
⚠ (:source lang=c header="generation_code.c" linestart=5 linenum:)
void debut_code(){
fprintf(fichier, "/* FICHIER GENERE PAR LE COMPILATEUR SIMPLE */\n\n");
fprintf(fichier, "#include<stdlib.h>\n#include<stdbool.h>\n#include<stdio.h>\n#include<string.h>\n\n");
fprintf(fichier, "int _esaisie;\n");
fprintf(fichier, "char _csaisie;\n");
fprintf(fichier, "char* _tsaisie;\n\n");
fprintf(fichier, "int main(void){\n");
}
(:sourcend:)
Je vous donne un programme pour tester les différentes saisies clavier que l'on peut faire. Il s'agit d'un simple programme qui calcule l'IMC :
⚠ (:source lang=text header="programme.simple" linenum:)
afficher "Programme de calcul de l'IMC\n";
b_Correct=faux;
faire
afficher "Votre nom : ";
saisir t_Nom;
afficher "Votre prénom : ";
saisir t_Prenom;
afficher "Votre âge : ";
saisir e_Age;
afficher "Votre taille (en m) : ";
saisir d_Taille;
afficher "Votre poids (en kg) : ";
saisir d_Poids;
afficher "Etes-vous un homme ? (si oui, répondez vrai, 1 ou true) ";
saisir b_Sexe;
(b_Sexe)?
afficher "Bonjour M. ";
:
afficher "Bonjour Mme ";
;
afficher t_Nom;
afficher " ";
afficher t_Prenom;
afficher ", vous avez ";
afficher e_Age;
afficher " ans, vous mesurez ";
afficher d_Taille;
afficher "m et pesez ";
afficher d_Poids;
afficher "kg. Votre IMC est de : ";
afficher d_Poids/(d_Taille*d_Taille);
afficher "kg/m².\n";
afficher "Est-ce correct ? (si oui, répondez vrai, 1 ou true) ";
saisir b_Correct;
supprimer t_Nom;
supprimer t_Prenom;
?(non b_Correct);
(:sourcend:)
Avec ce que j'ai saisi, cela donne pour moi :
⚠ (:source lang=text:)
Programme de calcul de l'IMC
Votre nom : TRIBUTSCH
Votre prénom : Thomas
Votre âge : 23
Votre taille (en m) : 182
Votre poids (en kg) : 79
Etes-vous un homme ? (si oui, répondez vrai, 1 ou true) oui
Bonjour Mme TRIBUTSCH Thomas, vous avez 23 ans, vous mesurez 182.000000m et pesez 79.000000kg. Votre IMC est de : 0.002385kg/m².
Est-ce correct ? (si oui, répondez vrai, 1 ou true) non
Votre nom : TRIBUTSCH
Votre prénom : Thomas
Votre âge : 23
Votre taille (en m) : 1.82
Votre poids (en kg) : 79
Etes-vous un homme ? (si oui, répondez vrai, 1 ou true) 1
Bonjour M. TRIBUTSCH Thomas, vous avez 23 ans, vous mesurez 1.820000m et pesez 79.000000kg. Votre IMC est de : 23.849777kg/m².
Est-ce correct ? (si oui, répondez vrai, 1 ou true) 1
(:sourcend:)
Voilà pour cette évolution qui rend désormais les programmes Simple un peu plus interactifs. La prochaine sera assez complexe je pense, on fera de la structure de données où on implémentera les tableaux.
<< Évolution 9 : Les nombres décimaux | Évolution 10 : Les entrées clavier | Prochainement Évolution 11 >>
Thomas Tributsch - (CC BY-NC-SA 3.0 FR)