Une calculatrice en C
Date : 13/05/2008
Fichier PDF du projet : Rapport projet.pdf
Sources du projet : Sources
Pré-requis : Langage C, Arbres binaires
Table des matières :
Introduction
Mise en oeuvre
De la commande à l’arbre
De l’arbre au calcul
Utilisation du programme
Introduction
On se propose de programmer une calculatrice évoluée en langage C qui gère les priorités implicites . Celle-ci devait fonctionner en deux modes (réel et complexe) et éventuellement gérer les opérations logiques (et, ou, non, etc), ainsi que l’ensemble des fonctions usuelles telles que sinus, exponentielle, . . . L’utilisateur pourra utiliser des variables prédéfinies et initialisées à zéro. Il aura aussi accès au dernier résultat calculé grâce à la variable ANS.
Le programme doit être capable de détecter les erreurs, telles que des parenthèses manquantes, confusion par la priorité (nous reviendrons sur ce point ci-après), fonctions non identifiées, division par zéro, etc. Il doit aussi afficher le type d’erreur rencontré, et le plus d’informations possible la concernant (type, endroit, proposition pour y remédier).
Faute de temps, le programme que nous avons réalisé ne fonctionne qu’en mode réel, et ne gère pas les opérations logiques. Le mode réel est cependant achevé.
Nous avons rapidement établi que le sujet s’apparentait à un problème de compilation : il s’agit ici de coder un interpréteur de commande. Nos recherches sur le sujetnous ont permis d’identifier deux grandes phases dans ce processus.
La première consiste à vérifier la commande entrée par l’utilisateur, et à la transformer en une structure permettant l’interprétation à proprement parler, qui est la seconde grande étape. Nous avons ensuite établit que cette structure devait être un arbre (en
l’occurrence binaire).
Ces deux étapes se divisent elles-mêmes en plusieurs autres plus petites, ce que nous allons détailler dans la section suivante.
Mise en oeuvre
De la commande à l’arbre
Notre programme commence par lancer un boucle infinie (de type while(1){}), qui
servira pour attendre une nouvelle commande de la part de l’utilisateur lorsque la commande
en cours aura été traitée.
On lit la commande entrée, si elle n’est pas “exit”, on ne sort pas de la boucle, et
on effectue ce qui suit.
Lorsque l’utilisateur envoie sa commande au programme, celle-ci n’est qu’une chaîne
de caractères que le programme ne peut pas comprendre. Il faut pour cela la découper
en mots, puis donner du sens à ces mots, avant de pouvoir effectuer le calcul. Cela
permet dans le même temps de vérifier deux choses :
- les mots entrés par l’utilisateur existent bel et bien dans le langage ;
- la commande ainsi formée est correcte : c’est à dire que toutes les règles d’écriture
(la grammaire) sont respectées.
On nomme ces vérifications l’analyse syntaxique et lexicale.
Pour réaliser ce découpage on créé une structure de mot que l’on appellera token. Cette structure contiendra :
- la sous chaîne de caractère lui correspondant ;
- son type, c’est à dire sa nature (nombre, opérateur, parenthèse, etc) ;
- l’adresse de la fonction qui s’appliquera à ce mot lors de l’interprétation1.
typedef enum {PAR_O, PAR_F ,OP_UN, OP_BIN ,VAR,NOMBRE,AFFECT,ERROR} tok_type ;
typedef enum {FALSE ,TRUE} bool_t ;
typedef double ( faffect ) ( char , double ) ;
typedef double ( fbin ) ( double , double ) ;
typedef double ( fun ) ( double ) ;
typedef double ( fval ) ( char ) ;
typedef union { fbin bin ; fun un ; fval val ; faffect aff ; } fptr ;
De l’arbre au calcul
On se souvient que lors de la création de nos tokens, nous avions pris la précaution
d’y adjoindre un champ contenant la fonction à appeler sur celui-ci pour l’interprétation.
Voici venu le temps de nous en servir.
L’interprétation de notre arbre est récursive. Pour chaque noeud, on appelle la fonction
qu’il contient et on l’applique sur l’interprétation du sous arbre de gauche, et sur
l’interprétation du sous arbre de droite. Ceci est le principe de base, mais comme les
tokens ne sont pas tous de même type, les fonctions a appeler sont de même, de type
différents : d’où l’union que l’on trouve dans la structure fptr, qui est le type pointeur
de fonction.
