# 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 <val>**: Pour les valeurs de type "string"
- **PUSHB <val>**: 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