26 mai 2026
Voici les grandes lignes, orientées maintenance.
Le projet est un petit interpréteur du langage Logo en C++ : il lit
un fichier .logo, le transforme en instructions internes,
puis exécute ces instructions pas à pas avec une tortue graphique
SFML.
Flux Principal
main.cpp lit deux arguments :
logo fichier.logo delai Le délai sert à ralentir
l’animation pour voir la tortue bouger.
Lexer.cpp découpe le texte en
tokens : mots-clés comme avance, droite,
pour, fin, nombres, noms de fonctions, signes
[ et ].
Program.cpp compile ces
tokens en objets Instruction. Par exemple :
avance 150 devient une instance de Move.
droite 90 devient une instance de Turn. Un nom
inconnu comme hex devient un appel de fonction
FunctionCall.
Interpreter.cpp exécute
une instruction à chaque appel de nextStep(). Il maintient
une pile d’exécution (myCallStack) pour gérer les appels de
fonctions Logo.
Turtle.cpp gère la fenêtre SFML, la position de la tortue, son angle, le stylo levé/baissé, et les lignes dessinées.
Architecture
La classe centrale abstraite est Instruction. Chaque commande Logo hérite d’elle et redéfinit principalement :
doExecute() : ce que fait l’instruction.doDisplay() : comment l’afficher.doGetNext() : comment choisir l’instruction
suivante.Les commandes existantes sont :
avance /
reculedroite /
gaucheleve
/ baissepour ... finPoints importants à connaître
Le mot-clé repete existe dans le lexer, mais n’est pas
implémenté dans Program::parseKeyword(). Donc un fichier
comme prog2.logo peut être reconnu
lexicalement, mais échouera à la compilation avec “Unimplemented
keyword”.
Les fonctions Logo ne peuvent être définies qu’au niveau principal. Le code interdit les fonctions imbriquées dans Program.cpp.
La tortue utilise des variables globales dans le namespace
turtle : position, direction, état du stylo, couleur. C’est
simple pour un projet pédagogique, mais si tu maintiens longtemps le
code, ce serait un bon candidat à encapsuler dans une classe.
Risques à surveiller
Un appel à une fonction inexistante peut provoquer un crash :
FunctionCall::doExecute() récupère un
Program*, mais ne vérifie pas s’il vaut
nullptr avant de faire prog->begin().
Un programme vide peut aussi poser problème : l’interpréteur démarre
avec begin() == end(), puis
currentInstruction() déréférence potentiellement une
position invalide.
Enfin, certaines erreurs de parsing peuvent déréférencer un token de
fin si l’erreur arrive en bout de fichier. C’est typiquement le genre de
bug qui apparaît avec un fichier incomplet comme avance
sans nombre derrière.
Pour ajouter une nouvelle commande, le chemin habituel est : ajouter
le mot-clé dans Lexer.h, créer une
classe héritant de Instruction ou
UnaryInstruction, puis la brancher dans
Program::parseKeyword().