Compare commits
15 Commits
76e2707156
...
master
Author | SHA1 | Date | |
---|---|---|---|
5c4e537017
|
|||
69d2bbaaf3
|
|||
e2e058c8ac
|
|||
d73a5be3f5
|
|||
024f721091
|
|||
d8f9ddcc4c
|
|||
de80ed0dba
|
|||
1015a0947e
|
|||
07fa61ef6c
|
|||
102f08c2cf
|
|||
a22bc69491
|
|||
ca495dfd9e
|
|||
312f4cd125
|
|||
1a181095c6
|
|||
948b335cb5
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,4 +9,6 @@ rapport.log
|
|||||||
rapport.pdf
|
rapport.pdf
|
||||||
rapport.synctex.gz
|
rapport.synctex.gz
|
||||||
rapport.toc
|
rapport.toc
|
||||||
|
rapport.out
|
||||||
Projet_compilation_anthony_debucquoy_2025.tar.xz
|
Projet_compilation_anthony_debucquoy_2025.tar.xz
|
||||||
|
|
||||||
|
@ -7,6 +7,12 @@ ajouter 3 dans l;
|
|||||||
|
|
||||||
afficher l;
|
afficher l;
|
||||||
|
|
||||||
|
entier x = 3;
|
||||||
|
|
||||||
|
entier y = 4;
|
||||||
|
|
||||||
|
entier z = x + 3 * y + 6 / 3;
|
||||||
|
|
||||||
#
|
#
|
||||||
# afficher x vaut y;
|
# afficher x vaut y;
|
||||||
# afficher x ne vaut pas y;
|
# afficher x ne vaut pas y;
|
||||||
@ -15,3 +21,11 @@ afficher l;
|
|||||||
# afficher x + 3, 3 + y;
|
# afficher x + 3, 3 + y;
|
||||||
# x = -x;
|
# x = -x;
|
||||||
# afficher x;
|
# afficher x;
|
||||||
|
|
||||||
|
# entier x = 2 + 3 * 4;
|
||||||
|
# entier y = 3 * 4 + 2;
|
||||||
|
|
||||||
|
# liste l = [1, 2, 3];
|
||||||
|
|
||||||
|
afficher l[2];
|
||||||
|
|
||||||
|
15
examples/boucles_var.spf
Normal file
15
examples/boucles_var.spf
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
entier x = 0;
|
||||||
|
tant que x < 10 faire {
|
||||||
|
entier y = x;
|
||||||
|
x = x + 1;
|
||||||
|
afficher x, y;
|
||||||
|
}
|
||||||
|
|
||||||
|
afficher "yess";
|
||||||
|
|
||||||
|
pour chaque entier i dans [1:5] faire {
|
||||||
|
entier y = i;
|
||||||
|
y = y + i;
|
||||||
|
x = x + i;
|
||||||
|
afficher x, y;
|
||||||
|
}
|
14
examples/errors.spf
Normal file
14
examples/errors.spf
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
blop x = blop #SyntaxError
|
||||||
|
|
||||||
|
# afficher y; #UnknownVariable
|
||||||
|
|
||||||
|
# texte z;
|
||||||
|
# afficher z; #UninitializedVariable
|
||||||
|
|
||||||
|
# texte a;
|
||||||
|
# texte a; #AlreadyDefined
|
||||||
|
|
||||||
|
# entier b = "test"; #IncompatibleType
|
||||||
|
|
||||||
|
# liste c = [0, 1, 2];
|
||||||
|
# afficher c[10]; #IndexError
|
9
examples/lists.spf
Normal file
9
examples/lists.spf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
liste premier = [1, 2, 3];
|
||||||
|
|
||||||
|
ajouter 4 dans premier;
|
||||||
|
|
||||||
|
liste deuxieme = [1:4];
|
||||||
|
|
||||||
|
liste troisième = [deuxieme[0]: 10];
|
||||||
|
|
||||||
|
liste quatrième = [1: troisième[6]];
|
@ -9,7 +9,7 @@ majeur = faux;
|
|||||||
afficher nom, age, majeur;
|
afficher nom, age, majeur;
|
||||||
|
|
||||||
|
|
||||||
#Ces lignes devraient donner une erreur
|
# Ces lignes devraient donner une erreur
|
||||||
# majeur = 42;
|
# majeur = 42;
|
||||||
|
|
||||||
# afficher majeur;
|
# afficher majeur;
|
||||||
|
6
examples/test.spf
Normal file
6
examples/test.spf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
texte name = "Anthony";
|
||||||
|
si name ne vaut pas "Anthony" alors {
|
||||||
|
afficher "C'est pas moi";
|
||||||
|
} sinon {
|
||||||
|
afficher "C'est moi";
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from modules.errors import SPFUnknownVariable, SPFUninitializedVariable, SPFAlreadyDefined, SPFIncompatibleType, SPFIndexError
|
||||||
|
|
||||||
trace_format = '\033[1m -> '
|
trace_format = '\033[1m -> '
|
||||||
reset_format = '\033[0m'
|
reset_format = '\033[0m'
|
||||||
|
|
||||||
@ -11,14 +13,16 @@ class Variables:
|
|||||||
"booléen": bool,
|
"booléen": bool,
|
||||||
"liste": list }
|
"liste": list }
|
||||||
|
|
||||||
def __init__(self, typ, value = None):
|
def __init__(self, typ, value=None):
|
||||||
assert typ in self.types.keys(), "Ce type de variable est inconnu"
|
assert typ in self.types.keys(), "Ce type de variable est inconnu"
|
||||||
self.type = typ
|
self.type = typ
|
||||||
assert self.checkType(value, typ), "Le type n'est pas équivalent"
|
if not self.checkType(value, typ):
|
||||||
self.value = value if (value is not None) else self.default(typ)
|
raise SPFIncompatibleType(value, self.type)
|
||||||
|
self.value = value
|
||||||
|
|
||||||
def set(self, value):
|
def set(self, value):
|
||||||
assert self.checkType(value, self.type), "Le type n'est pas équivalent"
|
if not self.checkType(value, self.type):
|
||||||
|
raise SPFIncompatibleType(value, self.type)
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -49,19 +53,24 @@ class Variables:
|
|||||||
self.trace = trace
|
self.trace = trace
|
||||||
|
|
||||||
def get(self, name):
|
def get(self, name):
|
||||||
assert name in self.variables, "la variable {name} n'éxiste pas"
|
if name not in self.variables:
|
||||||
|
raise SPFUnknownVariable(name)
|
||||||
|
if self.variables[name].value == None:
|
||||||
|
raise SPFUninitializedVariable(name)
|
||||||
if self.trace:
|
if self.trace:
|
||||||
print(f"{trace_format}accède {name}{reset_format}", file=sys.stderr)
|
print(f"{trace_format}accède {name}{reset_format}", file=sys.stderr)
|
||||||
return self.variables[name].value
|
return self.variables[name].value
|
||||||
|
|
||||||
def declare(self, typ, name, value=None):
|
def declare(self, typ, name, value=None):
|
||||||
assert name not in self.variables, "la variable {name} existe déjà"
|
if name in self.variables:
|
||||||
|
raise SPFAlreadyDefined(name)
|
||||||
self.variables[name] = self.Variable(typ, value)
|
self.variables[name] = self.Variable(typ, value)
|
||||||
if self.trace:
|
if self.trace:
|
||||||
print(f"{trace_format}déclare {name} = {value}{reset_format}", file=sys.stderr)
|
print(f"{trace_format}déclare {name} = {value}{reset_format}", file=sys.stderr)
|
||||||
|
|
||||||
def assign(self, name, value):
|
def assign(self, name, value):
|
||||||
assert name in self.variables, "la variable n'éxiste pas"
|
if name not in self.variables:
|
||||||
|
raise SPFUnknownVariable(name)
|
||||||
self.variables[name].set(value)
|
self.variables[name].set(value)
|
||||||
if self.trace:
|
if self.trace:
|
||||||
print(f"{trace_format}modifie {name} = {value}{reset_format}", file=sys.stderr)
|
print(f"{trace_format}modifie {name} = {value}{reset_format}", file=sys.stderr)
|
||||||
|
44
modules/errors.py
Normal file
44
modules/errors.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# args
|
||||||
|
# 0) variable name
|
||||||
|
# 1) list of lines of the stack trace
|
||||||
|
|
||||||
|
class SPFException(Exception):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = "Une erreur est survenue"
|
||||||
|
self.errorline = None
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return (f"[ligne {self.errorline}] " if self.errorline else "") + f"{self.msg}"
|
||||||
|
|
||||||
|
class SPFSyntaxError(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = "Une erreur de syntaxe est survenue"
|
||||||
|
|
||||||
|
class SPFUnknownVariable(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = f"la variable `{args[0]}` n'est pas déclarée"
|
||||||
|
|
||||||
|
class SPFUninitializedVariable(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = f"la variable `{args[0]}` n'est pas initialisée"
|
||||||
|
|
||||||
|
class SPFAlreadyDefined(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = f"la variable `{args[0]}` est déjà déclarée"
|
||||||
|
|
||||||
|
class SPFIncompatibleType(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = f"`{args[0]}` n'est pas de type `{args[1]}`"
|
||||||
|
|
||||||
|
class SPFIndexError(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = f"La liste `{args[0]}` ne posède pas d'élèment d'indexe {args[1]}"
|
||||||
|
|
||||||
|
|
BIN
rapport/images/notebook.jpg
Normal file
BIN
rapport/images/notebook.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 MiB |
@ -6,10 +6,15 @@
|
|||||||
\usepackage[pdftex]{graphicx}
|
\usepackage[pdftex]{graphicx}
|
||||||
\usepackage{amsmath, amsfonts, amssymb, amsthm}
|
\usepackage{amsmath, amsfonts, amssymb, amsthm}
|
||||||
\usepackage{fullpage}
|
\usepackage{fullpage}
|
||||||
|
\usepackage[inline]{enumitem}
|
||||||
|
\usepackage{hyperref}
|
||||||
|
\usepackage{fancyvrb}
|
||||||
|
|
||||||
\title{Rapport du projet de compilation \\ Umons 2024-2025}
|
\title{Rapport du projet de compilation \\ Umons 2024-2025}
|
||||||
\author{Debucquoy Anthony}
|
\author{Debucquoy Anthony}
|
||||||
|
|
||||||
|
\DefineShortVerb{\|}
|
||||||
|
|
||||||
\begin{document}
|
\begin{document}
|
||||||
|
|
||||||
\maketitle
|
\maketitle
|
||||||
@ -20,46 +25,196 @@
|
|||||||
|
|
||||||
\newpage
|
\newpage
|
||||||
|
|
||||||
\section{Consigne}
|
\section*{Préface}
|
||||||
% - Une description brève du projet;
|
|
||||||
|
|
||||||
\section{Grammaire}%
|
Ce document retrace le développement d'un compilateur de \textit{\textbf{S}imple \textbf{P}rogramme
|
||||||
\label{sec:la grammaire}
|
en \textbf{F}rançais} (SPF) dans le cadre du projet de compilation de L'\textit{Université de Mons}.
|
||||||
|
|
||||||
|
Ce projet est supposé être réalisé en groupe. Mais lorsque les consignes du projet été données,
|
||||||
|
j'ai rapidement souhaité me faire une idée de son ampleur en tentant une première version.
|
||||||
|
En restant sur ma lancée je suis arrivé très rapidement à un résultat satisfaisant à mes yeux et
|
||||||
|
pratiquement fonctionnelle (non exempt de bugs évidement). J'ai alors fait la demande à \textit{Mr.
|
||||||
|
DECAN Alexandre} notre assistant et la personne référente du projet.
|
||||||
|
Celui-ci m'a confirmé que je pourrais accomplir ce projet par moi-même.
|
||||||
|
|
||||||
|
\section{Consigne}
|
||||||
|
|
||||||
|
Il nous est demandé de développer un interpréteur pour le langage \textbf{SPF}, un langage de
|
||||||
|
programmation \textit{faiblement et statiquement typé} dans lequel les instructions sont en
|
||||||
|
français.
|
||||||
|
|
||||||
|
Voici une liste semi-exhaustive de ce qui nous est demandé.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Les lignes commençant par le caractère |#| sont ignorés
|
||||||
|
\item Les instruction se termine par le caractère |;|
|
||||||
|
\item les types suivants doivent être implémentés:
|
||||||
|
\begin{enumerate*}
|
||||||
|
\item |entier|
|
||||||
|
\item |texte|
|
||||||
|
\item |liste|
|
||||||
|
\item |booléen|
|
||||||
|
\end{enumerate*}
|
||||||
|
\item une déclaration est de la forme |<type> <nom>;| ou |<type> <nom> = <expression>;|
|
||||||
|
\item une assignation est de la forme |<nom> = <expression;|
|
||||||
|
\item Les noms de variables peuvent contenir des accents
|
||||||
|
\item Il est possible de créer une liste d'éléments consécutive avec |[a:b]| où a et b sont
|
||||||
|
les bornes de la liste
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Le langage contient également de nombreux opérateurs qui ne sont pas tous présents dans ce document,
|
||||||
|
car ils sont similaires à la majorité des langages. Il est cependant à noter que les mots sont traduit
|
||||||
|
de l'anglais au français. Par exemple, |true| deviens |vrai| en SPF.
|
||||||
|
On remarque également que l'accès à la liste commence avec un indice de 1. Ainsi, l'accès au deuxième
|
||||||
|
élément de la liste l (|liste l = [a, b, c];|) devra être effectué à l'aide de l'expression
|
||||||
|
|l[2]| et retournera l'élément |b|
|
||||||
|
L'ajout d'élément dans une liste se fait à l'aide de l'instruction\\
|
||||||
|
|ajouter <expression> dans <variable>|
|
||||||
|
L'affichage d'une expression à l'écran à l'aide de \\
|
||||||
|
|afficher <expression> (, <expression> ...);|
|
||||||
|
Finalement, les boucle |for| sont semblables à celle en python avec comme syntaxe \\
|
||||||
|
|<type> <variable> dans <expression> faire{...}|
|
||||||
|
On remarquera aussi que pour les variables initialisées dans les tests et boucles sont limités à leurs
|
||||||
|
portées.
|
||||||
|
|
||||||
|
\section{Grammaire}
|
||||||
|
|
||||||
% – Une description de la grammaire implémentée et des points sensibles
|
% – Une description de la grammaire implémentée et des points sensibles
|
||||||
% la concernant;
|
% la concernant;
|
||||||
|
|
||||||
\section{Approche}%
|
J'ai tenté d'inclure un maximum d'éléments vu en cours dans la grammaire. Ça n'était pas le cas dans
|
||||||
\label{sec:Approche}
|
un premier temps quand je tentais encore de voir l'étendue des capacités de la librairie et la grammaire a
|
||||||
|
donc été modifiée avec le temps. Je l'ai aussi réalisée bien avant que nous n'ayons vu tout le
|
||||||
|
contenu du cours. Je n'avais pas encore vu les grammaires LALR. Je pense malgré tout que
|
||||||
|
ça n'aurait pas changé grand chose à l'approche que j'ai actuellement.
|
||||||
|
|
||||||
|
Le programme est constitué d'instruction consécutive. La première ligne est donc naturellement.
|
||||||
|
\begin{Verbatim}[samepage=true, frame=single]
|
||||||
|
start: (instruction)*
|
||||||
|
\end{Verbatim}
|
||||||
|
où les instructions sont de la forme
|
||||||
|
\begin{Verbatim}[samepage=true, frame=single]
|
||||||
|
instruction: declaration ";"
|
||||||
|
| assignation ";"
|
||||||
|
| SHOW_KW expression ("," expression)* ";" -> afficher
|
||||||
|
| ADD_KW expression "dans" VARIABLE ";" -> append
|
||||||
|
| controls
|
||||||
|
\end{Verbatim}
|
||||||
|
|
||||||
|
Nous avons donc les 5 grandes catégories qui sont représentées par des instructions qui sont
|
||||||
|
terminées par un |;| (ou non dans le cas des instructions de contrôle)
|
||||||
|
|
||||||
|
Dans le but de respecter la consigne, le projet s'est déroulé avec l'option stricte activée tout le
|
||||||
|
long du développement. Ce qui m'a empêché de rédiger une grammaire ambigüe. Néanmoins, dans un
|
||||||
|
premier temps ma grammaire ne respectais pas du tout la priorité des opérateurs.
|
||||||
|
Après relecture du cours, j'ai pu ordonner mes opérateurs dans un tableau et ai réécrit ma grammaire
|
||||||
|
pour qu'elle soit non récursive gauche et que l'ordre des opérateurs soit respecté.
|
||||||
|
|
||||||
|
Pour lever la récursivité gauche, j'ai ajouté les analogues à chaque opérateur avec un u à la fin
|
||||||
|
(l'idée était d'avoir un \textbf{u} pour \textbf{u}nembiguoused mais par la suite je me suis rendu
|
||||||
|
compte que ce nom n'était pas très descriptif. Néanmoins, je ne l'ai pas changé par manque
|
||||||
|
d'imagination probablement.)
|
||||||
|
|
||||||
|
\begin{Verbatim}[samepage=true, frame=single]
|
||||||
|
logical: comparison logicalu?
|
||||||
|
logicalu: AND_OP logical
|
||||||
|
| OR_OP logical
|
||||||
|
\end{Verbatim}
|
||||||
|
|
||||||
|
Le reste du fichier est relativement standard et facile à comprendre.
|
||||||
|
|
||||||
|
\section{Approche}
|
||||||
|
|
||||||
% – Une explication de votre approche pour gérer :
|
% – Une explication de votre approche pour gérer :
|
||||||
% ∗Les variables, leur déclaration, leur type et l’affichage via --trace;
|
% ∗Les variables, leur déclaration, leur type et l’affichage via --trace;
|
||||||
% ∗Le test conditionnel de la forme si/sinon;
|
% ∗Le test conditionnel de la forme si/sinon;
|
||||||
% ∗La boucle tant que;
|
% ∗La boucle tant que;
|
||||||
% ∗La boucle pour chaque, incluant la gestion de la variable tempo-
|
% ∗La boucle pour chaque, incluant la gestion de la variable temporaire.
|
||||||
% raire.
|
|
||||||
|
|
||||||
\section{Erreurs}%
|
Ma première étape fut de prendre note de toutes les fonctions à implémenter dans le projet pour
|
||||||
\label{sec:Les erreurs}
|
pouvoir m'y référer au fur et à mesure (Figure~\ref{fig:notebook})
|
||||||
|
|
||||||
|
\begin{figure}[h]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.8\textwidth]{images/notebook.jpg}
|
||||||
|
\caption{Note avant projet.}
|
||||||
|
\label{fig:notebook}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Je souhaitais faire un système séparé pour gérer les variables. Au lieu de l'inclure dans le même
|
||||||
|
fichier, il m'a semblé judicieux d'en faire un système indépendant. (|./modules/Variables.py|)
|
||||||
|
Ce module est pratiquement un simple dictionnaire où les clés sont les noms des variables et les
|
||||||
|
valeurs sont des dictionnaires eux même contenant le type en clé et la valeur en valeur. Ce système
|
||||||
|
permet de rapidement trouver une variable dans le dictionnaire tout en gardant les informations sur
|
||||||
|
son contenue.
|
||||||
|
|
||||||
|
Le fait d'en faire un module séparé permet de bien définir toutes les interactions possibles avec
|
||||||
|
celle-ci (set, get, define, ...) tout en laissant la possibilité d'inclure des fonctionnalités
|
||||||
|
dynamiques (trace, dump, ...). Ce fût l'un des premiers élément développé qui est resté opérationnel
|
||||||
|
le long du projet.
|
||||||
|
|
||||||
|
Par la suite je me suis demandé comment devrais-je gérer les \textit{scope}. En effet, une
|
||||||
|
variable crée dans une boucle ne devrait, par exemple par rester disponible après la portée de
|
||||||
|
celle-ci. Je n'avais pas envisagé ce scénario au début de mon implémentation et j'ai donc trouvé
|
||||||
|
comme solution de faire une copie de mes variables disponibles avant le début de ma boucle et de les
|
||||||
|
restituer après.
|
||||||
|
|
||||||
|
Pour les tests conditionnel dans les boucles et les branchements, j'ai pu profiter de la puissance
|
||||||
|
de lark. \`A l'aide de |self.visit_children(el)|, et par effet cascade, cette expression sera
|
||||||
|
évaluée a |vrai| ou |faux|. En fonction de sa valeur et ainsi m'appuyer sur celle-ci pour
|
||||||
|
demander à python de faire mes branchements. J'ai été agréablement surpris de constater que cette expression
|
||||||
|
peut être évaluée plusieurs fois. Ce qui implique que ça ne pose pas de problème pour les boucles
|
||||||
|
|tant que|. Il suffit de faire correspondre les valeurs au code python.
|
||||||
|
|
||||||
|
Pour la gestion d'erreurs, j'ai, dans un premier temps, implémenté toutes les erreurs demandées dans
|
||||||
|
un module séparé. Par la suite, comme ces erreurs sont des classes, j'ai implémenté une classe
|
||||||
|
parent qui permet un affichage uniforme à l'aide de la méthode |__str__| qui lit l'attribut |msg|
|
||||||
|
(le message à afficher) et |errorline| (le numéro de ligne). Il suffit alors d'appeler ces erreurs
|
||||||
|
lorsqu'elle se produise.
|
||||||
|
|
||||||
|
\section{Erreurs}
|
||||||
|
|
||||||
% – Une description brève des erreurs connues et des solutions envisagées;
|
% – Une description brève des erreurs connues et des solutions envisagées;
|
||||||
|
|
||||||
\section{Difficultés}%
|
Je n'ai pour, l'instant pas trouvé d'erreurs évidentes. Lorsque c'était le cas, je me suis empressé de
|
||||||
\label{sec:Difficultés}
|
tenter de les corriger et pour l'instant ça n'a pas été trop compliqué au point que je ne le mette
|
||||||
|
de côté.
|
||||||
|
|
||||||
|
\section{Difficultés}
|
||||||
|
|
||||||
% – Une brève présentation des difficultés rencontrées et des solutions
|
% – Une brève présentation des difficultés rencontrées et des solutions
|
||||||
% implémentées/envisagées;
|
% implémentées/envisagées;
|
||||||
|
|
||||||
\section{Utilisation de l'IA}%
|
Le projet s'est principalement passé sans difficultés particulières. J'ai passé un petit moment à
|
||||||
\label{sec:Utilisation de l'IA}
|
faire en sorte que ma grammaire soit sans ambigüité et que l'ordre des opérateurs soit correcte.
|
||||||
|
|
||||||
% – Les points éventuels pour lesquels vous avez fait appel à une IA6
|
\section{Utilisation de l'IA}
|
||||||
% (ChatGPT, CoPilot, etc.);
|
|
||||||
|
|
||||||
\section{Répartition du travail}%
|
Lorsque je travaille pour apprendre je ne souhaite en général pas utiliser d'IA dans le but de me
|
||||||
\label{sec:Répartition du travail}
|
former mieux. Il m'arrive plus d'utiliser cet outil dans le but de me faciliter la tache pour un
|
||||||
|
travail fastidieux que je suis certains de savoir faire et dont je \textbf{peux} vérifier la fiabilité par
|
||||||
|
moi-même.
|
||||||
|
|
||||||
% – La répartition du travail au sein du groupe.
|
Ceci dis, lors de l'élaboration de ce projet, je me suis vu utiliser l'IA à un moment. J'ai tenté de
|
||||||
|
donner ma grammaire déjà écrite à chatgpt (modèle GPT-4o à travers \url{https://duck.ai/} ) en lui
|
||||||
|
demandant de me générer une série de programmes utilisant ce langage qui pourraient me servir de
|
||||||
|
test. L'objectif était d'avoir plus d'exemples de programmes que ceux données par la consigne.
|
||||||
|
|
||||||
|
Néanmoins, après très peu de temps je, me suis ravisé et ai simplement ignoré la réponse donnée, car les
|
||||||
|
propositions de programmes étaient bien trop simple et ne tentaient pas du tout de "piéger" mon
|
||||||
|
implémentation.
|
||||||
|
|
||||||
|
Pour être tout à fait transparent, c'est la seule et unique utilisation de l'IA que j'ai faite jusqu'à
|
||||||
|
maintenant, mais je compte utiliser LanguageTool à la fin de la rédaction de ce rapport pour m'aider
|
||||||
|
à corriger les éventuelles fautes commises dans ce document.
|
||||||
|
|
||||||
|
\section{Répartition du travail}
|
||||||
|
|
||||||
|
Comme précisé dans la préface de ce document, je me suis chargé de l'intégralité du projet. J'ai
|
||||||
|
commencé par me faire la main avec la librairie proposée (lark) par le tutoriel du parser json.
|
||||||
|
Ensuite le projet s'est très vite mis en place. Plusieurs révisions ont été nécessaires pour faire
|
||||||
|
correspondre le projet aux attentes, vous pouvez les consulter dans l'historique du repo git à
|
||||||
|
l'adresse \url{https://git.herisson.ovh/tonitch/compilation}
|
||||||
|
|
||||||
\end{document}
|
\end{document}
|
||||||
|
|
||||||
|
66
spf.lark
66
spf.lark
@ -6,35 +6,40 @@ instruction: declaration ";"
|
|||||||
| ADD_KW expression "dans" VARIABLE ";" -> append
|
| ADD_KW expression "dans" VARIABLE ";" -> append
|
||||||
| controls
|
| controls
|
||||||
|
|
||||||
expression: expressionleft // TODO: priorité des operator certainement fausse
|
// rule finishing by u are "UnambigiousED"
|
||||||
| operator
|
expression: logical
|
||||||
|
|
||||||
expressionleft: literal
|
logical: comparison logicalu?
|
||||||
|
logicalu: AND_OP logical
|
||||||
|
| OR_OP logical
|
||||||
|
|
||||||
|
comparison: sumterm comparisonu?
|
||||||
|
comparisonu: SAME_OP comparison
|
||||||
|
| DIFF_OP comparison
|
||||||
|
| LT_OP comparison
|
||||||
|
| LE_OP comparison
|
||||||
|
| GT_OP comparison
|
||||||
|
| GE_OP comparison
|
||||||
|
|
||||||
|
sumterm: multterm sumtermu?
|
||||||
|
sumtermu: PLUS_OP sumterm
|
||||||
|
| MINUS_OP sumterm
|
||||||
|
|
||||||
|
multterm: priority multtermu?
|
||||||
|
multtermu: TIMES_OP multterm
|
||||||
|
| DIVIDE_OP multterm
|
||||||
|
|
||||||
|
priority: finalterm
|
||||||
|
| finalterm "[" expression "]" -> list_get
|
||||||
|
| SIZE_OP finalterm
|
||||||
|
| NEG_OP finalterm
|
||||||
|
| NOT_OP finalterm
|
||||||
|
|
||||||
|
finalterm: "(" expression ")"
|
||||||
|
| literal
|
||||||
| list
|
| list
|
||||||
| range
|
| range
|
||||||
| VARIABLE -> variable
|
| VARIABLE -> variable
|
||||||
| "(" expression ")"
|
|
||||||
|
|
||||||
//any -> bool
|
|
||||||
operator: expressionleft SAME_OP expression -> equal
|
|
||||||
| expressionleft DIFF_OP expression -> unequal
|
|
||||||
//bool -> bool
|
|
||||||
| expressionleft AND_OP expression -> and_op
|
|
||||||
| expressionleft OR_OP expression -> or_op
|
|
||||||
| NOT_OP expression -> not_op
|
|
||||||
//int -> bool
|
|
||||||
| expressionleft LT_OP expression -> lt
|
|
||||||
| expressionleft LE_OP expression -> le
|
|
||||||
| expressionleft GT_OP expression -> gt
|
|
||||||
| expressionleft GE_OP expression -> ge
|
|
||||||
//int -> int
|
|
||||||
| expressionleft PLUS_OP expression -> plus
|
|
||||||
| expressionleft MINUS_OP expression -> minus
|
|
||||||
| expressionleft TIMES_OP expression -> time
|
|
||||||
| expressionleft DIVIDE_OP expression -> divide
|
|
||||||
| NEG_OP expression -> neg
|
|
||||||
// string/list -> string/list
|
|
||||||
| SIZE_OP expression -> sizeof
|
|
||||||
|
|
||||||
?type: BOOL_TYPE
|
?type: BOOL_TYPE
|
||||||
| INT_TYPE
|
| INT_TYPE
|
||||||
@ -45,8 +50,8 @@ declaration: type VARIABLE (EQUAL_SIGN expression)?
|
|||||||
|
|
||||||
assignation: VARIABLE EQUAL_SIGN expression
|
assignation: VARIABLE EQUAL_SIGN expression
|
||||||
|
|
||||||
loop: "tant" "que" expression "faire" "{" (instruction)* "}" -> while_loop
|
loop: "tant" "que" expression "faire" "{" instruction_seq "}" -> while_loop
|
||||||
| "pour" "chaque" type VARIABLE "dans" expression "faire" "{" (instruction)* "}" -> for_loop
|
| "pour" "chaque" type VARIABLE "dans" expression "faire" "{" instruction_seq "}" -> for_loop
|
||||||
|
|
||||||
?literal: ENTIER -> entier
|
?literal: ENTIER -> entier
|
||||||
| booleen
|
| booleen
|
||||||
@ -54,12 +59,13 @@ loop: "tant" "que" expression "faire" "{" (instruction)* "}" -> while_loop
|
|||||||
|
|
||||||
list: "[" expression? ("," expression)* "]"
|
list: "[" expression? ("," expression)* "]"
|
||||||
|
|
||||||
range: "[" expression? ":" expression? "]"
|
range: "[" expression ":" expression "]"
|
||||||
|
|
||||||
controls: test
|
controls: test
|
||||||
| loop
|
| loop
|
||||||
|
|
||||||
test: "si" expression "alors" "{" instruction* "}" ("sinon" "{" instruction* "}")?
|
test: "si" expression "alors" "{" instruction_seq "}" ("sinon" "{" instruction_seq "}")?
|
||||||
|
instruction_seq: (instruction*)
|
||||||
|
|
||||||
?booleen: TRUE_KW -> true
|
?booleen: TRUE_KW -> true
|
||||||
| FALSE_KW -> false
|
| FALSE_KW -> false
|
||||||
@ -99,6 +105,8 @@ GE_OP: ">="
|
|||||||
|
|
||||||
CONC_OP: "+"
|
CONC_OP: "+"
|
||||||
SIZE_OP: "taille"
|
SIZE_OP: "taille"
|
||||||
|
LBRAC: "["
|
||||||
|
RBRAC: "]"
|
||||||
|
|
||||||
ADD_KW: "ajouter"
|
ADD_KW: "ajouter"
|
||||||
SHOW_KW: "afficher"
|
SHOW_KW: "afficher"
|
||||||
|
196
spf.py
196
spf.py
@ -8,6 +8,7 @@ import lark
|
|||||||
import sys
|
import sys
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from modules.Variables import Variables
|
from modules.Variables import Variables
|
||||||
|
from modules.errors import *
|
||||||
|
|
||||||
class SPFInterpreter(lark.visitors.Interpreter):
|
class SPFInterpreter(lark.visitors.Interpreter):
|
||||||
def __init__(self, trace=False):
|
def __init__(self, trace=False):
|
||||||
@ -15,13 +16,32 @@ class SPFInterpreter(lark.visitors.Interpreter):
|
|||||||
self.variables = Variables(trace)
|
self.variables = Variables(trace)
|
||||||
|
|
||||||
def while_loop(self, el):
|
def while_loop(self, el):
|
||||||
print("TODO: while")
|
old = self.variables.variables.copy()
|
||||||
cond = el.children[0]
|
while self.visit_children(el.children[0])[0]:
|
||||||
instr = el.children[1:]
|
self.visit_children(el.children[1])
|
||||||
print(cond.pretty())
|
self.variables.variables = old.copy()
|
||||||
|
|
||||||
def for_loop(self, el):
|
def for_loop(self, el):
|
||||||
print("TODO: for")
|
type = el.children[0].value
|
||||||
|
name = el.children[1].value
|
||||||
|
old = self.variables.variables.copy()
|
||||||
|
try:
|
||||||
|
self.variables.declare(type, name)
|
||||||
|
except SPFException as e:
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
old_inloop = self.variables.variables.copy()
|
||||||
|
|
||||||
|
target = self.visit_children(el.children[2])[0]
|
||||||
|
for i in target:
|
||||||
|
try:
|
||||||
|
self.variables.assign(name, i)
|
||||||
|
except SPFException as e:
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
self.visit_children(el.children[3])
|
||||||
|
self.variables.variables = old_inloop.copy()
|
||||||
|
self.variables.variables = old.copy()
|
||||||
|
|
||||||
def afficher(self, el):
|
def afficher(self, el):
|
||||||
ligne = ""
|
ligne = ""
|
||||||
@ -31,87 +51,123 @@ class SPFInterpreter(lark.visitors.Interpreter):
|
|||||||
|
|
||||||
def append(self, el):
|
def append(self, el):
|
||||||
(_, toadd, var) = self.visit_children(el);
|
(_, toadd, var) = self.visit_children(el);
|
||||||
|
try:
|
||||||
var_val = self.variables.get(var.value)
|
var_val = self.variables.get(var.value)
|
||||||
|
except SPFException as e:
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
|
||||||
var_val.append(toadd)
|
var_val.append(toadd)
|
||||||
|
|
||||||
def declaration(self, el):
|
def declaration(self, el):
|
||||||
type = el.children[0].value
|
type = el.children[0].value
|
||||||
name = el.children[1].value
|
name = el.children[1].value
|
||||||
value = self.visit_children(el.children[3])[0] if len(el.children) >= 3 else None
|
value = self.visit_children(el.children[3])[0] if len(el.children) >= 3 else None
|
||||||
|
try:
|
||||||
self.variables.declare(type, name, value)
|
self.variables.declare(type, name, value)
|
||||||
|
except SPFException as e:
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
|
||||||
def assignation(self, el):
|
def assignation(self, el):
|
||||||
name = el.children[0].value
|
name = el.children[0].value
|
||||||
assert el.children[1].value == "=" and el.children[2].data == "expression", "Unexpected"
|
assert el.children[1].value == "=" and el.children[2].data == "expression", "Unexpected"
|
||||||
value = self.visit_children(el.children[2])[0]
|
value = self.visit_children(el.children[2])[0]
|
||||||
|
try:
|
||||||
self.variables.assign(name, value)
|
self.variables.assign(name, value)
|
||||||
|
except SPFException as e:
|
||||||
def equal(self, el):
|
e.errorline = el.meta.line
|
||||||
(left, sign, right) = self.visit_children(el)
|
raise e
|
||||||
return left == right
|
|
||||||
|
|
||||||
def unequal(self, el):
|
|
||||||
(left, sign, right) = self.visit_children(el)
|
|
||||||
return left != right
|
|
||||||
|
|
||||||
def and_op(self, el):
|
|
||||||
(left, sign, right) = self.visit_children(el)
|
|
||||||
return left and right
|
|
||||||
|
|
||||||
def or_op(self, el):
|
|
||||||
(left, sign, right) = self.visit_children(el)
|
|
||||||
return left or right
|
|
||||||
|
|
||||||
def not_op(self, el):
|
|
||||||
(sign, right) = self.visit_children(el)
|
|
||||||
return not right
|
|
||||||
|
|
||||||
def lt(self, el):
|
|
||||||
(left, sign, right) = self.visit_children(el)
|
|
||||||
return left < right
|
|
||||||
|
|
||||||
def le(self, el):
|
|
||||||
(left, sign, right) = self.visit_children(el)
|
|
||||||
return left <= right
|
|
||||||
|
|
||||||
def gt(self, el):
|
|
||||||
(left, sign, right) = self.visit_children(el)
|
|
||||||
return left > right
|
|
||||||
|
|
||||||
def ge(self, el):
|
|
||||||
(left, sign, right) = self.visit_children(el)
|
|
||||||
return left >= right
|
|
||||||
|
|
||||||
|
|
||||||
def plus(self, el):
|
|
||||||
(left, sign, right) = self.visit_children(el)
|
|
||||||
return left + right # Cool ça fonctionne pour les str
|
|
||||||
|
|
||||||
def minus(self, el):
|
|
||||||
(left, sign, right) = self.visit_children(el)
|
|
||||||
return left - right
|
|
||||||
|
|
||||||
def time(self, el):
|
|
||||||
(left, sign, right) = self.visit_children(el)
|
|
||||||
return left * right
|
|
||||||
|
|
||||||
def divide(self, el):
|
|
||||||
(left, sign, right) = self.visit_children(el)
|
|
||||||
return left / right
|
|
||||||
|
|
||||||
neg = lambda self, el:-self.visit_children(el)[1]
|
|
||||||
|
|
||||||
sizeof = lambda self, el:len(self.visit_children(el)[1])
|
|
||||||
|
|
||||||
|
|
||||||
def expression(self, el):
|
def expression(self, el):
|
||||||
return self.visit_children(el)[0]
|
return self.visit_children(el)[0]
|
||||||
|
|
||||||
def expressionleft(self, el):
|
def logical(self, el):
|
||||||
|
result = self.visit_children(el)
|
||||||
|
if len(result) < 2:
|
||||||
|
return result[0]
|
||||||
|
if result[1][0].type == "AND_OP":
|
||||||
|
return result[0] and result[1][1]
|
||||||
|
elif result[1][0].type == "OR_OP":
|
||||||
|
return result[0] or result[1][1]
|
||||||
|
assert "Unreachable"
|
||||||
|
|
||||||
|
def comparison(self, el):
|
||||||
|
result = self.visit_children(el)
|
||||||
|
if len(result) < 2:
|
||||||
|
return result[0]
|
||||||
|
if result[1][0].type == "SAME_OP":
|
||||||
|
return result[0] == result[1][1]
|
||||||
|
elif result[1][0].type == "DIFF_OP":
|
||||||
|
return result[0] != result[1][1]
|
||||||
|
elif result[1][0].type == "LT_OP":
|
||||||
|
return result[0] < result[1][1]
|
||||||
|
elif result[1][0].type == "LE_OP":
|
||||||
|
return result[0] <= result[1][1]
|
||||||
|
elif result[1][0].type == "GT_OP":
|
||||||
|
return result[0] > result[1][1]
|
||||||
|
elif result[1][0].type == "GE_OP":
|
||||||
|
return result[0] >= result[1][1]
|
||||||
|
assert "Unreachable"
|
||||||
|
|
||||||
|
def sumterm(self, el):
|
||||||
|
result = self.visit_children(el)
|
||||||
|
if len(result) < 2:
|
||||||
|
return result[0]
|
||||||
|
if result[1][0].type == "PLUS_OP":
|
||||||
|
return result[0] + result[1][1]
|
||||||
|
elif result[1][0].type == "MINUS_OP":
|
||||||
|
return result[0] - result[1][1]
|
||||||
|
assert "Unreachable"
|
||||||
|
|
||||||
|
def multterm(self, el):
|
||||||
|
result = self.visit_children(el)
|
||||||
|
if len(result) < 2:
|
||||||
|
return result[0]
|
||||||
|
if result[1][0].type == "TIMES_OP":
|
||||||
|
return result[0] * result[1][1]
|
||||||
|
elif result[1][0].type == "DIVIDE_OP": # Division entière car nous ne gérons pas les flotants
|
||||||
|
return result[0] // result[1][1]
|
||||||
|
assert "Unreachable"
|
||||||
|
|
||||||
|
def priority(self, el):
|
||||||
|
result = self.visit_children(el)
|
||||||
|
if len(result) < 2:
|
||||||
|
return result[0]
|
||||||
|
elif result[0].type == "SIZE_OP":
|
||||||
|
return len(result[1])
|
||||||
|
elif result[0].type == "NEG_OP":
|
||||||
|
return -result[1]
|
||||||
|
elif result[0].type == "NOT_OP":
|
||||||
|
return not result[1]
|
||||||
|
|
||||||
|
def list_get(self, el):
|
||||||
|
result = self.visit_children(el)
|
||||||
|
try:
|
||||||
|
return result[0][result[1] - 1] # Index start at 1 (like lua)
|
||||||
|
except IndexError:
|
||||||
|
e = SPFIndexError(result[0], result[1])
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def finalterm(self, el):
|
||||||
return self.visit_children(el)[0]
|
return self.visit_children(el)[0]
|
||||||
|
|
||||||
def variable(self, el):
|
def variable(self, el):
|
||||||
|
try:
|
||||||
return self.variables.get(el.children[0].value)
|
return self.variables.get(el.children[0].value)
|
||||||
|
except SPFException as e:
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def test(self,el):
|
||||||
|
old = self.variables.variables.copy()
|
||||||
|
if self.visit_children(el.children[0])[0]:
|
||||||
|
self.visit_children(el.children[1])
|
||||||
|
elif len(el.children) >= 3:
|
||||||
|
self.visit_children(el.children[2])
|
||||||
|
self.variables.variables = old.copy()
|
||||||
|
|
||||||
|
|
||||||
# Literals
|
# Literals
|
||||||
string = lambda self, el: el.children[0][1:-1]
|
string = lambda self, el: el.children[0][1:-1]
|
||||||
@ -119,6 +175,10 @@ class SPFInterpreter(lark.visitors.Interpreter):
|
|||||||
true = lambda self, _: True
|
true = lambda self, _: True
|
||||||
false = lambda self, _: False
|
false = lambda self, _: False
|
||||||
|
|
||||||
|
def range(self, el):
|
||||||
|
(left, right) = self.visit_children(el)
|
||||||
|
return list(range(left, right+1))
|
||||||
|
|
||||||
def dump(self):
|
def dump(self):
|
||||||
self.variables.dump()
|
self.variables.dump()
|
||||||
|
|
||||||
@ -139,11 +199,16 @@ def main():
|
|||||||
args = arg_parser.parse_args()
|
args = arg_parser.parse_args()
|
||||||
|
|
||||||
with open("spf.lark") as grammar:
|
with open("spf.lark") as grammar:
|
||||||
spf_parser = lark.Lark(grammar, parser="lalr", strict=True, debug=True)
|
spf_parser = lark.Lark(grammar, parser="lalr", strict=True, debug=True, propagate_positions=True)
|
||||||
|
|
||||||
with open(args.spf_file) as spf_input:
|
with open(args.spf_file) as spf_input:
|
||||||
program = spf_input.read()
|
program = spf_input.read()
|
||||||
|
try:
|
||||||
parsed = spf_parser.parse(program)
|
parsed = spf_parser.parse(program)
|
||||||
|
except lark.UnexpectedInput as u:
|
||||||
|
e = SPFSyntaxError()
|
||||||
|
e.errorline = u.line
|
||||||
|
raise e
|
||||||
|
|
||||||
if args.pretty:
|
if args.pretty:
|
||||||
print(parsed.pretty())
|
print(parsed.pretty())
|
||||||
@ -155,7 +220,6 @@ def main():
|
|||||||
if args.dump:
|
if args.dump:
|
||||||
interpreter.dump()
|
interpreter.dump()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user