Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ncfavier/pgo
Tiny Go compiler for the compilation project at ENS
https://github.com/ncfavier/pgo
compiler golang
Last synced: 21 days ago
JSON representation
Tiny Go compiler for the compilation project at ENS
- Host: GitHub
- URL: https://github.com/ncfavier/pgo
- Owner: ncfavier
- License: isc
- Created: 2020-10-04T12:07:57.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2020-10-04T12:09:56.000Z (over 4 years ago)
- Last Synced: 2024-11-07T00:49:02.771Z (2 months ago)
- Topics: compiler, golang
- Language: Haskell
- Homepage:
- Size: 268 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# pgo
Ce projet est réalisé en Haskell, et dépend des bibliothèques et extensions fournies par le compilateur [GHC](https://www.haskell.org/ghc/).
## Analyse lexico-syntaxique
J'utilise la bibliothèque [parsec](https://hackage.haskell.org/package/parsec), basée sur les combinateurs de parseurs (*parser combinators*), pour l'analyse lexicale et syntaxique.
La principale difficulté de cette partie a été l'insertion automatique de point-virgules. Je maintiens une valeur booléenne pendant le parsage, indiquant la présence d'un point-virgule automatique : quand la valeur est à `True`, le seul moyen de parser quoi que ce soit est d'appeler `semicolon` directement.
Il y a sans doute des façons plus élégantes de faire ça, notamment en séparant l'analyse lexicale et l'analyse syntaxique.
## Compilation
J'ai choisi de faire le typage et la production de code en une seule passe, dans le module `Compile`.
Les arbres de syntaxe abstraite sont définis dans le module `AST`.
Le module `Pack` fournit un type `Pack` permettant de représenter une zone contiguë en mémoire dans laquelle sont stockés des objets, éventuellement nommés.
Le module `X86_64` fournit des fonctions pour la génération de code assembleur.
La compilation globale a lieu dans la monade `Compiler`, composée des [transformateurs de monades](https://en.wikipedia.org/wiki/Monad_transformer) suivants :
- `Except TypeError` pour les erreurs de typage ;
- `WriterT String` pour la production de code ;
- `StateT GlobalState` pour l'état global.La compilation des fonctions a lieu dans la monade `FunctionCompiler`, qui rajoute un niveau `StateT FunctionState` par-dessus la monade `Compiler`.
La structure de donnée utilisée pour les associations (structures, champs, fonctions, variables, etc.) est [`Data.Map`](https://hackage.haskell.org/package/containers-0.6.2.1/docs/Data-Map-Strict.html), qui est implémentée avec des arbres binaires de recherche équilibrés.
La compilation débute avec `compileFile` et se déroule ainsi, dans les grandes lignes :
- on ajoute les structures dans l'environnement, en deux temps :
- d'abord, on ajoute les champs de chaque structure, sans information de taille ; on en profite pour vérifier l'unicité des noms de structures
- puis on construit un `Pack` pour les champs chaque structure, récursivement, en gardant une trace explicite de la pile d'appels récursifs afin de détecter les cycles
- on ajoute les fonctions dans l'environnement :
- on vérifie l'unicité des noms
- on vérifie que la fonction `main` a la bonne signature
- on construit un `Pack` pour les arguments, et un pour les valeurs de retour
- on compile le point d'entrée du programme C
- on compile les fonctions : pour chaque fonction,
- on ajoute les paramètres de la fonction à la portée initiale
- on compile le corps de la fonction
- on vérifie que toutes les branches de la fonction ont atteint une instruction `return`, le cas échéant
- on compile les littéraux de chaîne de caractères
- on vérifie qu'il y a une fonction `main`
- on vérifie que `fmt` est utilisé, s'il est importéLe type de `nil` est représenté par `Pointer Any`, où `Any` est un type fictif égal à n'importe quel type ; c'est une forme ultra-simpliste d'unification où les types sont unifiés par simple comparaison d'égalité.
Le code produit fait usage des [étiquettes locales](https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels-1).
La bibliothèque standard du C est utilisée pour l'affichage (`printf` et `putchar`) et pour l'allocation sur le tas (`sbrk`).
Les variables locales sont allouées sur la pile, sauf si elles sont utilisées comme opérande de l'opérateur `&`, auquel cas elles sont allouées sur le tas. Les variables allouées sur le tas sont représentées par un pointeur sur la pile.
Toutes les variables sont initialement mises à zéro, puisque c'est la valeur par défaut pour chacun des types de base.
Les booléens sont représentés par 0 (`false`) ou 1 (`true`).
La comparaison des chaînes de caractères ne nécessite que de comparer les adresses, puisque toute chaîne de caractères est soit la chaîne vide (représentée par 0), soit un pointeur vers un *unique* littéral associé au contenu de la chaîne dans le segment de données.