/tpc-compiler

[ARCHIVED 2017] A compiler for a C-like programming language built using the old Flex and Bison tokenizer and parser generator

git clone https://src.euxane.eu/tpc-compiler

Branches
master 2018-06-06

UPEM / Compilation / Projet

Compilateur TPC (Très Petit C).

Compilation

Dépendances

Licence

Projet distribué sous licence Creative Commons BY-NC-SA 4.0. Tous droits réservés aux auteurs du projet.

Other documents

doc/rapport.md

Usage

Après un make, ./tcompil < prog.tpc [-o prog.asm]

Pour faciliter les tests, utiliser make test FILE_TEST=test_file test_file est en réalité un fichier d’extension .tpc placé dans le répertoire res. (exemple: res/test_file.tpc). Ceci génère un executable test_file dans le répertoire out.

Fonctions fournies par le langage

Fonctions de lecture

Fonction d’écriture

Analyse lexicale

L’analyse lexicale est réalisée avec Flex et est contenue dans le fichier tpc.lex.

Lexèmes

Les lexèmes ont été définis au sein de ce fichier :

Symbole Description Token associé
= opérateur d’affectation
+ opérateur d’addition ADDSUB
- opérateur de soustraction ou moins unaire ADDSUB
* multiplication DIVSTAR
/ et % division et reste de la division entière DIVSTAR
! négation booléenne
==, != opérateur de comparaison EQ
<, >, <= ,>= opérateur de comparaison ORDER
&& opérateur booléen AND
|| opérateur booléen OR
; et , le point virgule et la virgule
(, ), {, }, [ et ] les parenthèses, les accolades et les crochets

Analyse syntaxique

La grammaire est celle fournie sur la plateforme elearning.

Résolution des conflits

Cette grammaire génère un conflit empiler/réduire pour la règle

Instr   : IF '('Exp')' Instr
        | IF '('Exp')' Instr ELSE Instr

Par exemple :

IF '('Exp')' IF '('Exp')' Instr ELSE Instr

Faut il empiler ELSE ? Ou réduire IF '('Exp')' Instr ? Pour résoudre le conflit, il faut gérer les precedences. Nous avons choisi de rentre le ELSE plus précédant que ')' qui est le dernier terminal. Ainsi, Bison va choisir d’empiler en priorité.

Un autre conflit existe, sur l’associativité de ,:

ListExp : ListExp ',' Exp
        | Exp ';'

Par exemple :

ListExp ',' ListExp ',' ListExp

Doit-on reduire à gauche ? Ou a droite ? Dans les langages tel que le C, l’associativité se fait à gauche. On déclare donc %left , pour l’indiquer à Bison.

Analyse sémantique

Le compilateur gère l’analyse sémantique. Par exemple l’opération tab[1] * 3 est légale mais 'a' * 'b' ne l’est pas. La grammaire l’accepte, mais l’analyse sémantique à parti des attributs interdits. De la même manière des caractères ne peuvent pas être des booléens.

Génération de code cible

Gestion de la mémoire

Pour des raisons de simplicité, il a été choisi de travailler principalement sur la pile. Son utilisation simplifie le changement de contexte à l’entrée et à la sortie de fonctions, en plus de permettre la réduction aisée des expressions lors de l’exécution.

Fonctions

Fonction principale

Le programme doit être doté d’une fonction void main(void) qui sera le point d’entrée du programme. La forme alternative entier main(void) est aussi acceptée par convention. La valeur retournée est ignorée et l’exécutable renverra toujours 0.

Passage de paramètres

Il a été choisi d’effectuer le passage des paramamètres simples (de types entier et caractere) par copie pour des raisons de simplicité et de sécurité, pour permettre une certaine isolation des contextes lors de l’entrée dans une fonction.

Tous les paramètres sont implicitement convertis en type entier, la vérification du typage des paramètres s’avérant difficile. De plus, il n’est pas possible de passer un tableau en paramètre par référence.

Retour de valeur

Les fonction valuées retournent leur valeur en suivant les conventions d’appel des fonctions C, c’est-à-dire en la plaçant dans le registre rax. L’appelant récupère ensuite cette valeur pour pouvoir la stocker dans la pile.

L’analyse statique des branchements étant un problème relativement complexe, il a été choisi de retourner une valeur par défaut (-1) en cas d’absence d’instruction de retour dans une fonction valuée.

Améliorations enviseageables

Récupération sur erreur

Nous pourrions envisager de parser entièrement le fichier et de ne pas s’arrêter dès la première erreur de syntaxe comme le fait gcc. Il est possible de réaliser ceci en modifiant le code de yyerror() par exemple.

Suppression du code partiellement généré

Une erreur de compilation entraîne l’arrêt de la génération de code, pouvant laisser un fichier de sortie partiellement généré. Il serait préférable de le supprimer en cas d’erreur. Cette gestion s’appuierait sur yyerror() et le retour de la fonction yyparse(). Lorsque le code de retour du compilateur est identifiable à une erreur, on supprimerait le .asm généré afin de ne pas fournir à l’utilisateur un programme assembleur frelaté.