trafi

Transducteurs finis
git clone git://git.asteride.xyz/trafi.git
Journaux | Fichiers | Références

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:
MMakefile | 4++--
Aatt.c | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aatt.h | 36++++++++++++++++++++++++++++++++++++
Mattvtfd.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; }