Programmation impérative, projet 2021

Dates et principe

Cette page peut être mise à jour, avec informations complémentaires, précisions, questions bonus, etc. Pensez à y revenir souvent.

Projet à rendre pour le 9/1/2022 à 23h59, aucun retard ne sera toléré.
Des soutenances pourront être organisées ensuite.

Mise à jour du 6/12/2021 : le fichier word.aut a été corrigé.

Lire tout le sujet.

Un rendu de projet comprend : Avez-vous lu tout le sujet ?

Protocole de dépôt

Vous devez rendre

rassemblés dans une archive tar gzippée identifiée comme votre_prénom_votre_nom.tgz.
La commande devrait ressembler à :
tar zcvf randolph_carter.tgz rapport.pdf fichiers.c autres_truc_éventuels.c
N'OUBLIEZ surtout PAS de mettre le nom identifiant l'archive (donc nouveau) en PREMIER.
Lisez le man ! et testez le contenu de votre archive (une commande comme par exemple :
tar tvf randolph_carter.tgz doit lister les fichiers et donner leur taille).

Toute tentative de fraude (plagiat, etc.) sera sanctionnée. Si plusieurs projets ont des sources trop similaires (y compris sur une partie du code uniquement), tous leurs auteurs se verront attribuer la note 0/20. En particulier, il faudra prendre soin de ne pas publier son travail sur un dépôt public (en tout cas pas avant la date de fin de rendu). On évitera également de demander (ou de donner) des conseils trop précis à ses camarades (y compris des promotions précédentes), ces conseils ayant pu être donnés à plusieurs personnes. Les rendus seront comparés deux à deux.

Procédure de dépôt
Vous devez enregistrer votre archive tgz dans le dépôt dédié au cours IPI (ipi-2021) en vous connectant à http://exam.ensiie.fr. Ce dépôt sera ouvert jusqu'au 9 janvier inclus.


Contexte

Le but de ce projet est d'implanter un programme qui exécute des automates LR(1). Ces derniers servent à reconnaître des langages de programmation.

Les automates vous seront fournis dans un certain format décrit ci-après. Il n'est pas nécessaire pour ce projet de comprendre par quel moyen de tels automates ont été obtenus.

Interface

Le programme va lire un fichier (dont le nom sera passé en paramètre de l'exécutable) qui contiendra une description de l'automate. Il va ensuite lire des lignes sur l'entrée standard. Après chaque ligne lue, il indiquera si la ligne est correcte pour le langage correspondant à l'automate, auquel cas il affichera Accepted, sinon il affichera Rejected.

Par exemple, si le fichier dyck.aut contient une description d'un automate correspondant au langage des mots bien parenthésés, on aura le comportement suivant (en bleu les entrées de l'utilisateur) :

$ ./automaton dyck.aut
File "dyck.aut" correctly read. Please enter your inputs.
()
Accepted
()(())
Accepted
(()
Rejected
(()()))
Rejected
(blabla)
Rejected

Automates LR1

Définition et exécution

Un automate LR1 est donné par les éléments suivants :

Un tel automate fonctionne à l'aide d'une pile d'état. Initialement, cette pile contiendra un unique élément, à savoir l'état initial. Au cours de l'exécution, l'état courant sera celui situé au sommet de la pile.

On essaie de reconnaître un mot en commençant par la lettre la plus à gauche.

Le comportement d'un tel automate est le suivant : si l'état courant est s et la lettre d'entrée est c, on agit en fonction de action(s,c) :

Si c'est Rejette :
on s'arrête, le mot ne fait pas partie du langage.
Si c'est Accepte :
on s'arrête, le mot fait partie du langage.
Si c'est Décale :
on empile décale(s,c), on passe à la lettre suivante de l'entrée.
Si c'est Réduit :
si réduit(s) vaut (n, A), on commence par dépiler n états ; l'état courant devient alors s' ; on empile alors branchement(s',A) ; on ne passe pas à la lettre suivante de l'entrée.

Par exemple, l'automate suivant permet de reconnaître les mots formés d'une séquence quelconque de lettres alphabétiques, suivie par un retour à la ligne :

L'état initial est l'état Q0, on représente décale par les flèches noires, réduit par les bleues et branchement par les rouges.

Sur l'entrée "Ok\n", en partant de la pile contenant Q0, on va avoir l'exécution suivante :

Implémentation et format du fichier

Les états seront représentés par un entier sur un octet. (On aura donc au maximum 256 états.) L'état initial sera l'état 0.

Pour l'alphabet d'entrée mais aussi pour les symboles non-terminaux, on utilisera les caractères dont le code ASCII est compris entre 0 et 127 inclus.

Les actions seront représentées par des entiers : Rejette = 0, Accepte = 1, Décale = 2 et Réduit = 3.

On supposera qu'on ne dépilera jamais plus de 256 états lors d'une réduction, on pourra donc représenter la première composante de réduit(s,c) sur un octet.

Un fichier contenant une description d'un automate LR(1) respectera le format suivant :

Exemples d'entrées

Vous trouverez ici des entrées pour tester votre programme.
Mots
C'est l'automate donné en exemple ci-dessus. Il reconnaît les mots formés d'une séquence quelconque de lettres alphabétiques, suivie par un retour à la ligne.
Mots 2
Un autre automate qui reconnaît le même langage.
Langage de Dyck
Cet automate reconnaît les mots utilisant uniquement les parenthèses ( et ) et qui sont bien parenthésés.
Expressions arithmétiques
Cet automate reconnaît les expressions formées de constantes entières (positives ou négatives), des opérateurs binaires + - * / et des parenthèses ( et ), avec la possibilité de mettre des espaces.
D'autres exemples seront probablement proposés d'ici la date de rendu.

Bonus

Conseils

Pour la récupération d'une entrée de l'utilisateur, plutôt que faire un scanf directement, il vaut parfois mieux récupérer une ligne en entier avec fgets puis utiliser sscanf dessus ; on peut utiliser la suite de commandes suivantes :
char buf[256];
 .
 .
 .
fgets(buf, 256, stdin);
sscanf(buf, "format", ...);
Vous devez avoir lu jusqu'ici avant de commencer.