commit 668018f8f0df88242b59e796e74f10216f16575a
parent 05d44bb40760cd5b395c56e93da9dd15bc00eba9
Auteur: Loïc Daignault-Pichette <loic@asteride.xyz>
Date: Sun, 15 Feb 2026 16:39:19 -0500
Lire et écrire au format .att
Diffstat:
| M | Makefile | | | 4 | ++-- |
| A | att.c | | | 217 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | att.h | | | 36 | ++++++++++++++++++++++++++++++++++++ |
| M | attvtfd.c | | | 12 | +++++++++++- |
4 fichiers modifiés, 266 insertions(+), 3 suppressions(-)
diff --git a/Makefile b/Makefile
@@ -9,8 +9,8 @@ COPTS = -Wall -Wextra -Wpedantic -std=c99 -O0 #-DNDEBUG -O3 -flto
tout: attvtfd
-attvtfd: attvtfd.o tampinf.o tfm.o
- ${CC} ${COPTS} -o "$@" attvtfd.o tampinf.o tfm.o
+attvtfd: attvtfd.o tampinf.o tfm.o att.o
+ ${CC} ${COPTS} -o "$@" attvtfd.o tampinf.o tfm.o att.o
net:
rm -rf attvtfd *.o *.core
diff --git a/att.c b/att.c
@@ -0,0 +1,217 @@
+#include <assert.h>
+#include <string.h>
+
+#include "att.h"
+#include "tampinf.h"
+#include "tfm.h"
+
+/* Séparateurs de champs. */
+#define SEP_ERR -1 /* Pas un séparateur à proprement parler. */
+#define SEP_EOF 0
+#define SEP_NL 1
+#define SEP_TB 2
+
+/* Lire le prochain champ du fichier f, dans le tampon t de taille max.
+ * Renvoie le séparateur qui a terminé le champ lu, et si ce séparateur est
+ * SEP_TB ou SEP_NL, c’est-à-dire, si t a été modifié, place dans noct le nombre
+ * de caractères écrits dans t. Si SEP_ERR est renvoyé, c’est que le tampon
+ * était trop petit pour contenir le champ. */
+static int lire_champ(FILE *f, char *t, size_t max, size_t *noct);
+
+/* Convertir la chaîne de caractères t de contenant noct octets en nombre.
+ * Renvoie le nombre, ou -1 en cas d’erreur. */
+static ssize_t nombre(char *t, size_t noct);
+
+/* Ajouter un symbole à un transducteur.
+ * Renvoie 0, ou -1 en cas d’erreur. Doit être utilisé un lieu de tfm_symb_id()
+ * pour intercepter les symboles spéciaux comme @0@. */
+static int ajt_symb(struct tfm *tfm, char *t, size_t noct, ssize_t *id);
+
+/* Préparer la lecture du fichier f.
+ * La routine se fait peut-être une idée trop stricte du format .att et n’est
+ * pas très flexible. */
+struct tfm *
+att_lire(FILE *f)
+{
+ char champ[TFM_TLL_SYMB_MAX]; /* Tampon d’un champ. */
+ struct tfm *tfm; /* Tranducteur. */
+ ssize_t es, ed; /* États source et destination. */
+ ssize_t se, ss; /* Symboles d’entrée et de sortie. */
+ ssize_t n; /* Nombre. */
+ size_t noct; /* Nombre d’octets écrits dans champ. */
+
+ assert(f);
+
+ if ((tfm = tfm_init()) == NULL)
+ return NULL;
+
+ for (;;) {
+ /* Premier champ: état source ou état final. */
+ switch (lire_champ(f, champ, TFM_TLL_SYMB_MAX, &noct)) {
+ case SEP_ERR:
+ goto erreur;
+ case SEP_EOF:
+ /* C’est la fin du fichier et la dernière ligne est complète: tout va
+ * bien. */
+ return tfm;
+ case SEP_NL:
+ /* La ligne ne contient qu’un champ. L’état qu’il contient doit être
+ * marqué comme final. */
+ if ((n = nombre(champ, noct)) < 0)
+ goto erreur;
+ if (tfm_mfinal(tfm, n) < 0)
+ goto erreur;
+ continue;
+ case SEP_TB:
+ /* La ligne déclare une transition. Sauvegarder le champ dans es. */
+ if ((es = nombre(champ, noct)) < 0)
+ goto erreur;
+ }
+
+ /* Deuxième champ: état suivant. */
+ switch (lire_champ(f, champ, TFM_TLL_SYMB_MAX, &noct)) {
+ case SEP_ERR: case SEP_EOF: case SEP_NL:
+ /* Une ligne ne peut contenir que deux champs. */
+ goto erreur;
+ case SEP_TB:
+ /* Sauvegarder le champ dans ed. */
+ if ((ed = nombre(champ, noct)) < 0)
+ goto erreur;
+ }
+
+ /* Troisième champ: symbole d’entrée. */
+ switch (lire_champ(f, champ, TFM_TLL_SYMB_MAX, &noct)) {
+ case SEP_ERR: case SEP_EOF: case SEP_NL:
+ /* Une ligne ne peut contenir que trois champs. */
+ goto erreur;
+ case SEP_TB:
+ /* Sauvegarder le champ dans se. */
+ if (ajt_symb(tfm, champ, noct, &se) < 0)
+ goto erreur;
+ }
+
+ /* Quatrième champ: symbole de sortie. */
+ switch (lire_champ(f, champ, TFM_TLL_SYMB_MAX, &noct)) {
+ case SEP_ERR:
+ case SEP_EOF: /* Doit se terminer sur une nouvelle ligne. */
+ case SEP_TB: /* Il ne peut y avoir plus de quatre champs. */
+ goto erreur;
+ case SEP_NL:
+ /* Sauvegarder le champ dans ss. */
+ if (ajt_symb(tfm, champ, noct, &ss) < 0)
+ goto erreur;
+ }
+
+ if (tfm_ajt(tfm, es, ed, se, ss) < 0)
+ goto erreur;
+ }
+
+ return tfm;
+
+erreur:
+ tfm_libr(tfm);
+ return NULL;
+}
+
+int
+att_ecrire(FILE *f, struct tfm *tfm)
+{
+ struct etat etat;
+ struct trans trans;
+ size_t ietat, itrans;
+ char symb[TFM_TLL_SYMB_MAX];
+ ssize_t lngr;
+
+ assert(f);
+ assert(tfm);
+
+ /* Parcourir les états. */
+ for (ietat = 0; tampinf_dedans(tfm->etats, ietat); ietat++) {
+ if (tampinf_obt(tfm->etats, ietat, 1, &etat) < 0)
+ return -1;
+ /* Parcourir les transitions. */
+ for (itrans = etat.trans; etat.ntrans--; itrans = trans.prch) {
+ if (tampinf_obt(tfm->trans, itrans, 1, &trans) < 0)
+ return -1;
+ fprintf(f, "%ld\t%ld\t", ietat, trans.ed);
+ if ((lngr = tfm_symb_obt(tfm, trans.se, symb,
+ TFM_TLL_SYMB_MAX)) < 0)
+ return -1;
+ fwrite(symb, 1, lngr, f);
+ fputc('\t', f);
+ if ((lngr = tfm_symb_obt(tfm, trans.ss, symb,
+ TFM_TLL_SYMB_MAX)) < 0)
+ return -1;
+ fwrite(symb, 1, lngr, f);
+ fputc('\n', f);
+ }
+ /* Afficher les transitions finales. */
+ if (tfm_tfinal(tfm, ietat))
+ fprintf(f, "%ld\n", ietat);
+ }
+
+ return 0;
+}
+
+static int
+lire_champ(FILE *f, char *t, size_t max, size_t *noct)
+{
+ int c;
+ size_t i;
+
+ assert(f);
+ assert(t);
+ assert(noct);
+
+ /* Si déjà la fin, renvoyer EOF. */
+ if ((c = getc(f)) == EOF)
+ return SEP_EOF;
+
+ /* Ajouter les caractères dans le tampon jusqu’au premier séparateur ou
+ * jusqu’à ce que le tampon soit plein. */
+ for (i = 0; c != '\t' && c != '\n' && c != EOF; t[i++] = c, c = getc(f))
+ if (i >= max)
+ return SEP_ERR;
+
+ *noct = i;
+
+ switch (c) {
+ case EOF: return SEP_EOF;
+ case '\n': return SEP_NL;
+ case '\t': return SEP_TB;
+ }
+
+ assert(0);
+}
+
+static ssize_t
+nombre(char *t, size_t noct)
+{
+ size_t i;
+ size_t n;
+
+ assert(t);
+
+ for (i = n = 0; i < noct; n = n * 10 + (t[i++] - '0')) {
+ if (t[i] < '0' || t[i] > '9')
+ return -1;
+ }
+
+ return n;
+}
+
+static int
+ajt_symb(struct tfm *tfm, char *t, size_t noct, ssize_t *id)
+{
+ assert(tfm);
+ assert(t);
+ assert(id);
+
+ /* Intercepter les symboles spéciaux. */
+ if (noct == 3 && strncmp(t, "@0@", noct) == 0) {
+ *id = TFM_SYMB_EPSILON;
+ return 0;
+ }
+
+ return tfm_symb_id(tfm, t, noct, id);
+}
diff --git a/att.h b/att.h
@@ -0,0 +1,36 @@
+#ifndef ATT_H
+#define ATT_H
+
+#include <stdio.h>
+
+#include "tfm.h"
+
+/* Le format .att est composé de lignes et de champs. Les champs d’une même
+ * ligne sont séparés par un caractère de tabulation. Il existe deux types de
+ * lignes: l’un déclare une transition, l’autre un état comme final. On les
+ * distingue selon le nombre de champs que contient la ligne. Les transitions
+ * comptent quatre champs, qui sont: le numéro de l’état source, le numéro de
+ * l’état destination, le symbole d’entrée et le symbole de sortie. En voici un
+ * exemple:
+ *
+ * 0 1 ᵘ u
+ *
+ * Certains symboles sont interprétés différemment par foma. La présente mise en
+ * œuvre reconnaît @0@ comme le symbole epsilon (ε). D’autres sont à venir.
+ *
+ * Les lignes qui déclarent un état comme final ne comptent qu’un seul champ,
+ * qu’occupe le numéro de l’état final. Par exemple:
+ *
+ * 1
+ */
+
+/* Lire le fichier f au format .att.
+ * Initialise et renvoie un transducteur au format struct tfm, un NULL en cas
+ * d’erreur. */
+struct tfm *att_lire(FILE *f);
+
+/* Écrire une représentation .att du transducteur tfm dans le fichier f.
+ * Renvoie 0, ou -1 en cas d’erreur. */
+int att_ecrire(FILE *f, struct tfm *tfm);
+
+#endif
diff --git a/attvtfd.c b/attvtfd.c
@@ -1,7 +1,17 @@
-#include "tampinf.h"
+#include "att.h"
+#include "tfm.h"
int
main(void)
{
+ struct tfm *tfm;
+
+ if ((tfm = att_lire(stdin)) == NULL) {
+ fprintf(stderr, "attvtf: erreur lors de la lecture\n");
+ return 1;
+ }
+
+ tfm_libr(tfm);
+
return 0;
}