# Rapport Compilateur ## Introduction Le but de ce travail pratique était de réaliser le dévelopemment d'un compilateur dans l'idée de créer un langage de programmation informatique. Le langage décrit dans ce document se veut un langage simple, reprenant les différents aspects primitifs dont à besoin un langage. Il se veut fortement typé avec vérification à la compilation. Il est couplé à un préprocesseur pour la gestion de symbole, aussi bien interne, externe que lié à l'OS de compilation. Le langage a été développé dans le but d'être compilé pour la SVM créée par Matthieu Amiguet. Celle-ci à aussi subit quelques modifications (principalement de l'ajout d'opCode) détailées dans la suite de ce document. ## Détails du langage ### Spécifications #### Préprocesseur Permettre des options de compilations conditionnelles - **#DEFINE {sym}:** permet de définir un symbole et de tester son existence par la suite - **#DEFINE {sym}={value}:** permet de définir un symbole de type clef, valeur afin de remplacer des éléments dans le codes - **#IF {sym} #ELSE:** Permet de tester l'existence d'un symbole - Ajout automatique des symboles correspondant à l'OS utilisé (Windows, Mac, Linux, ...) - Ajout de symbole par la ligne de commande (ex: compile.py -D debug) #### Syntaxique - Boucles - for -> i 0...10 (range) - do while - Types - int - float - bool - string - If else - Opérateur de comparaisons - gt - st - eq - not - Commentaires - // - /* */ #### Sémantique Vérification des types d'opérandes ### Grammaires ``` program : statement ";" | statement ";" program statement : structure | declaration | assignment | PRINT expression structure : WHILE expression "{" program "}" | DO "{" program "}" WHILE expression | FOR "(" statement ";" expression ";" statement ")" "{" program "}" | IF expression "{" program "}" | IF expression "{" program "}" ELSE "{" program "}" declaration : type_specifier assignment assignment : IDENTIFIER "=" expression expression : INT_VALUE | FLOAT_VALUE | BOOL_VALUE | STR_VALUE | IDENTIFIER | ADD_OP expression %prec UNARY | "(" expression ")" | expression ADD_OP expression | expression MUL_OP expression | expression GT expression | expression ST expression | expression EQ expression | expression NOT expression type_specifier : INT | FLOAT | BOOL | STR ``` ### Préprocesseur Le préprocesseur est l'élément lancé au tout début de la compilation, c'est un ensemble d'expression régulière qui vont parcourir le fichier d'entré afin de recomposer un fichier ne contenant que les parties spécifiées par différentes règles. Ce code traité est ensuite envoyé à la suite de la compilation pour son analyse lexicale, syntaxique et sémantique. Avec ce préprocesseur il est ainsi possible d'effectuer de la compilation conditionnelle, afin de permettre une base de code plus unique avec seulement un traitement des spécificitées des différentes plateformes visées. Cela peut aussi être intéréssant pour gérer des modes de retour d'informations en mode debug et non pas en release (logs, vérifications, etc...). ### Modifications de la SVM Pour mener à bien ce projet, il a été nécéssaire de modifier la SVM fournie. En effet, celle-ci ne comportait pas certaines fonctionnalités requises. #### Types Pour la gestion des types au sein de la SVM il a été choisi de créer différents opCode en fonction du type de données pour en confirmer la consistance au sein de la VM. Ainsi `PUSHV` et `PUSHC` on été rejoins par: - **PUSHS &lt;val&gt;**: Pour les valeurs de type "string" - **PUSHB &lt;val&gt;**: Pour les valeurs de type "booléen" #### Opérateurs de comparaisons La version initiale de la SVM ne comportait pas d'opCode pour les comparaisons. Ainsi, les suivants ont été ajouté: - **GT**: Plus grand que - **ST**: Plus peti que - **EQ**: Égal à - **NOT**: Différent de Chacun d'entre eux utilise les deux dernières valeurs du stack pour la comparaison. Ensuite le résultat et retourné sous forme de booléen sur la pile. ## Exemples ### Langage #### Définition de variables ``` int a = 0; float b = 0.0; str c = 'salut'; bool = true; ``` #### Opérations Les opérations de bases qui composent le langage sont; l'addition, la soustraction, la multiplication, la division et enfin le modulo. ```c // Addition a = a + b; // Soustraction a = a - b; // Multiplication a = a * b; // Division a = a / b; // Modulo a = a % b; ``` ##### Tableau des types Ce tableau représente les types utilisables ensembles pour les opérations de bases | | int | float | bool | str | |-------|:-----:|:-------:|:------:|:-----:| | **int** | <font color='green'>**V**</font> | <font color='green'>**V**</font> | X | X | | **float** | <font color='green'>**V**</font> | <font color='green'>**V**</font> | X | X | | **bool** | X | X | <font color='green'>**V**</font> | X | | **str** | X | X | X | <font color='green'>**V**</font> | #### Comparaisons Les opérateurs de comparaisons sont utilisables sur le type int, float et bool. Il se composent des 4 opérations de bases. ```c bool result = False; int a = 10; int b = 12; result = (a < b); // True result = (a > b); // False result = (a == b); // False result = (a != b); // True ``` Il est important de noter que le type effectif d'une opération de comparaison devient un booléen. Ainsi, il est nécéssaire de l'entourer de parenthèse dans certain cas afin que le compilateur comprenne la nature de l'opération. Ainsi il est important d'écrire: ```c result = (a + 10) < b; // Valide ``` et non pas: ```c result = a + 10 < b; // Erreur de type à la compilation ``` #### If else Ceux-ci n'intègrent pas la possibilité d'effectuer des else if. ```c bool flag = False; if (flag == True) { print 'Flag is true and only if'; } if (flag == True) { print 'Flag is true'; } else { print 'Flag is false'; }; ``` #### While ```c int n = 2; while(n < 101) { print n; n = n + 1; }; ``` #### For Il est important de noté que la formulation i++, n'existe pas au sein de ce langage. ```c for (int i = 2; i < ((n + 1) / 2) ; i = i + 1) { if ( (n % i) == 0) { flag = True; print 'not prime'; // We could save time if the break op have been existing }; }; ``` #### Commentaires ```c // Simple ligne ``` ```c /* Multi-ligne */ ``` ### Préprocesseur #### Symbole interne Ces symboles sont définies au sein même d'un programme. ```c #DEFINE test #IF test print 'test'; #ENDIF ``` #### Symbole de plateforme Ces symboles sont dépendants de la plateforme sur laquelle est lancé le préprocesseur. - **MacOS** = Darwin - **Windows** = Windows - **Linux** = Linux ```c #IF Windows print 'windows'; #ELSE print 'unix'; #ENDIF ``` #### Symbole externe Ces symboles sont définis à l'appel du compilateur grâce à l'argument `-D <symb>`. ``` python .\compiler.py .\code.txt -D debug ``` ```c #IF debug print 'debug'; #ENDIF ``` ## Guide utilisateur ### Pré-requis - Python 3.7 - Ply https://www.dabeaz.com/ply/ ### Utilisation Après avoir écrit du code il suffit d'utiliser la commande suivante qui s'occupera d'écrire le fichier .vm: ``` python .\compiler.py <fichierTexte> [-D <symbole>] ``` Pour exécuter un fichier compilé il suffit de lancer la commande suivant: ``` python .\svm.py <fichierCompilé> ``` ### Exemples Le projet est livré avec quelques exemples de code que voici: #### Code1 Petit code pour montrer différents aspects du langage. #### Code2 Petit code pour montrer l'utilisation des boucles. #### WrongTypes La compilation de ce programme retourne une erreur de type à l'affectation de la variable a. #### Pyramid Ce programme "print" une pyramide dans pour montrer l'imbriquation de boucles "for", la possibilité de partager leur paramètre et la concaténation de string. #### Prime Ce programme afficher dans la console tout les nombres premiers jusqu'à 100. Il sert à montrer l'utilisation de notre langage pour résoudre certains problèmes de bases. #### Preprocessor Ce programme est la pour montrer l'utilisation des différents aspects du préprocesseur, il est intéréssant de le compiler de plusieurs façons. En effet la sortie sera différentes en fonction de l'OS utilisé et de la présence du paramètre `-D debug` à la compilation. ## Problèmes rencontrés - Lors de la compilation, le compilateur renvoyait des Warnings intitulés "SHIFT/Reduce conflict". Ce message apparaissait car l'ordre de priorité des opCodes n'était pas le bon. Après une ré-organisation du code, ces warnings ont disparus. - La vérification des types a posé quelques problèmes. En effet, ne fonctionnant au début qu'avec uniquement deux opérandes. Celle-ci a dû être réécrite de façon récursive pour garantir une vérification des tout les types d'une opération. - Il a aussi été nécéssaire de prendre en compte que le type d'une opération dépend aussi de sont opérateur. Ainsi, pour les opérateurs booléens, une telle action retourne un type booléen et non pas le type de ses opérandes. ## Bogues connues - Pas de vérifications pour les opérateurs au sein d'un type, en effet rien n'empêche d'effectuer des opérations sur des types dont cette opérations n'est pas implémentées (ex: string - string implique une erreur au runtime). - La vérification de type retourne parfois une exception alors qu'elle n'a pas lieu d'être en fonction de l'ordre des opérandes et des parenthèses. ## Améliorations futures - Résoudre le problème des ; (points-virgules) à mettre à chaque fin de ligne - Casting des types - Cast implicite lors d'un print ## Conclusion Nous avons pris bien du plaisir à faire ce projet de compilateur. Cela nous a permis de bien comprendre comment une machine fonctionnait en arrière-plan et nous a entraîné à l'exercer nous-même. Ce projet pourra nous servir dans nos carrières professionnelles futures lors du besoin de créer un langage spécifique à un programme par exemple. ###### Luca Verardo, Alexandre Bianchi - 2020 HE-Arc