1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
\chapter{Description}
\section{Introduction}
Étant donné la complexité relative de ce projet, nous avons choisi d'utiliser au maximum la compilation séparée.
De plus, nous voulions réaliser une interface en mode texte, mais que le projet soit facilement extensible
à des développements ultérieurs comme par exemple la réalisation d'une interface graphique pour X-Window, c'est pourquoi
nous avons réuni l'essentiel des fonctions dans une librairie et que nous avons un fichier principal contenant la fonction
main() de taille ridicule. Enfin cette programmation modulaire permet d'organiser facilement son code source et de pouvoir
partager aisément le travail entre les différents étudiants.
\section{Modules de calcul}
\subsection{scalaires.c}
Ce module réalise des opérations sur les scalaires. Comme prévu dans l'énoncé du sujet, les scalaires que nous avons
considéré sont des rationnels supportant toutes les opérations classiques, auxquelles nous avons ajouté différents "constructeurs"
comme le constructeur à partir de deux entiers ou le constructeur à partir d'un double. Étant donné que les constructeurs simplifient
les fractions, toutes les fractions stockées en mémoire le sont toujours sous forme réduite. On dispose enfin d'une fonction utilisée
dans les affichages qui convertit un rationnel en une chaîne de caractères formatée et dépendant de certains paramètres comme la variable
globale display.
\subsection{polynom.c}
Ce module nous permet d'effectuer toutes les opérations basiques sur les polynômes. On dispose d'un constructeur, d'un constructeur
par recopie et d'un destructeur. La structure de données utilisée est une liste simplement chaînée de monômes triés par degrés
décroissants ( il est à noter que le tri est effectué par construction, on ne fait jamais appel explicitement à une fonction
de type tri de monôme ). Cet ordre de tri est très pratique pour des algorithmes comme la division ou l'addition.
Comme dans le module précédent, nous y avons ajouté une routine de conversion d'un polynôme en une chaîne de caractères formatée
dépendant notamment de la variable globale smartprint.
Toutes les opérations sur les polynomes sont appelés dans pile.c où l'on gère la pile d'opérandes, notamment par la fonction act\_pile.
\subsection{fonctions.c}
Dans ce module nous avons regroupé toutes les fonctions "avancées" sur les polynômes, notamment la dérivation et l'intégration,
mais aussi les fonctions nécessaires à la gestion des variables ou de l'environnement.
Tous les appels à ces fonctions se font à partir de pile.c dans la fonction act\_pile, case OP\_FUNC\_CALL.
\section{Module d'interprétation des lignes de commande}
\subsection{parser.c}
Ce module est la clé de voûte de notre ensemble. Il est décrit plus en détail dans la section suivante. Il s'occupe de lire
une chaîne qui lui a été passé par l'interface, et va la décomposer en une série d'appels vers une pile, codée dans un autre module.
Il va simplement dégager les opérateurs et les opérandes pour les fournir à la pile sous la forme d'une pile polonaise inversée.
\subsection{pile.c}
Ce module possède principalement deux fonctions (push\_pile() et act\_pile()) qui sont appelées par le parser. La fonction
push\_pile() va tenter de dégager une opérande de l'entrée qui est faite sous forme d'une string. En particulier, elle va tenter
d'analyser l'entrée suivant trois critères:
\begin{itemize}
\item Est-ce un entier?
\item Est-ce un flottant?
\item Est-ce une variable déjà enregistrée?
\end{itemize}
En cas d'échec de ces trois critères, elle considérera qu'il s'agit d'un symbole. La fonction act\_pile() va simplement
dépiler suffisamment d'opérandes de la pile, effectuer l'opération demandée, et réinsérer le résultat sur la pile.
\subsection{numbers.c}
Ce tout petit module contient des fonctions qui peuvent convertir n'importe quelle chaîne contenant un nombre en son équivalent.
char\_to\_number() est capable de convertir un entier écrit en décimal, octal ou hexadécimal. char\_to\_rat() se charge de lire
un nombre flottant et de le stocker directement dans notre structure 'rationnel'.
\section{Modules de saisie et d'affichage}
\subsection{interface.c}
Ce module est l'interface de saisie. Les fonctions implémentées ne sont pas portables, au sens où elles dépendent du
terminal utilisé, en particulier pour l'usage des touches droites, gauches, F3, CTRL-C, insert, home et end. Mais dans
tous les cas, elles seront capable de lire une entrée "classique", ne présentant aucun caractère particulier. Une fois la
touche 'entrée' enfoncée, elle va appeler la fonction parse\_line(), provenant du module parser.c.
\subsection{terminal.c}
Ces quelques fonctions nous permettent d'initialiser le terminal à notre convenance. Ces fonctions sont typiques UNIX et
nous servent à imiter le comportement DOS de la fonction getche() en ce qui concerne l'entrée au clavier. En particulier,
une fois la fonction initterm() appelée, il n'y a plus d'écho à l'écran des touches enfoncées, et surtout, chaque touche
enfoncée est disponible immédiatement dans le buffer d'entrée.
\section{Module de gestion des erreurs}
\subsection{exceptions.c}
Ce module nous sert à "imiter" le comportement C++ des exceptions. Dans un premier temps, nous pouvons faire appel aux fonctions
pushcontext() et popcontext() pour empiler ou dépiler un contexte d'erreur. Ensuite, un appel à exception() va se traduire
par un affichage sur stderr de la pile de contexte en cours, et du message d'erreur donné en paramètre. Un deuxième paramètre
sert à indiquer le niveau de l'exception (erreur fatale ou non). Si une erreur fatale se produit, le programme sort directement.
Si une erreur non fatale se produit, toutes les piles sont vidées et la main est rendue à l'interface.
|