Automate associé
à une expression rationnelle

Dépendances

Voici le fichier Makefile (avec les règles et les macros prédéfinies standard):
CC = gcc
LEX = flex
YACC = bison
LDLIBS = -ly -ll

automate: automate.c autolex.c
	$(CC) -o $@ $@.c $(LDLIBS)

automate.c: automate.y
	$(YACC) -o $@ $<

autolex.c: autolex.l

Analyseur lexical

Fichier autolex.l :
%%
[a-z]	{yylval = feuille (yytext [0]); return ID; }
[ \t]+	;
.|\n	return yytext [0];

Analyseur syntaxique

Fichier automate.y :
%{
typedef struct {
  int vide, premier, dernier;
} attribut;

#define YYSTYPE attribut

attribut feuille (char id);
attribut noeud (char operateur, attribut gauche, attribut droite);
void init ();
void afficher (attribut a);
void transition (int source, int but);

%}

%token ID

%%
axiome	: axiome expr '\n'	{afficher ($2); init ();}
	| axiome '\n'
	| /* vide */
	;
	
expr	: expr '|' terme	{$$ = noeud ('|', $1, $3); }
	| terme
	;
	
terme	: terme facteur		{$$ = noeud ('.', $1, $2);
				 transition ($1.dernier, $2.premier); }
	| facteur
	;
	
facteur	: facteur '*'		{$$.vide = 1;
				 transition ($1.dernier, $1.premier); }
	| facteur '+'		{transition ($1.dernier, $1.premier); }
	| facteur '?'		{$$.vide = 1; }
	| '(' expr ')'		{$$ = $2; }
	| ID
	;
%%

#include "autolex.c"

#define EMAX 1024
#define GMAX 1024
#define PMAX 64

typedef struct {
  int source, but;
} arc;

static int
  elibre = 0,
  glibre = 0,
  position = 1,
  ensemble [EMAX];

static char lettre [PMAX];
static arc graphe [GMAX];

void debordement (char *s) {
  printf ("Débordement de la table '%s'\n", s);
  exit (1);
}

void init () {
  elibre = glibre = 0;
  position = 1;
}

/* Ne pas appeler une fonction "union": mot réservé en C */

int fusion (int a, int b) {
  int i, debut = elibre;
  
  for (i = a; ensemble [i] >= 0; i++) elibre++;
  for (i = b; ensemble [i] >= 0; i++) elibre++;
  if (elibre >= EMAX)
    debordement ("ensemble");
  
  elibre = debut;
  for (i = a; ensemble [i] >= 0; i++)
    ensemble [elibre++] = ensemble [i];
  for (i = b; ensemble [i] >= 0; i++)
    ensemble [elibre++] = ensemble [i];
  ensemble [elibre++] = -1;
  return debut;
}

void afficher (attribut a) {
  int i, p, q;
  
  printf ("Premier :");
  for (i = a.premier; ensemble [i] >= 0; i++)
    printf ("%3d", ensemble [i]);
  printf ("\n");

  printf ("Dernier :");
  for (i = a.dernier; ensemble [i] >= 0; i++)
    printf ("%3d", ensemble [i]);
  printf ("\n\n");
  
  /* Création de l'état initial */
  
  if (elibre + 2 > EMAX)
    debordement ("ensemble");
  
  ensemble [elibre++] = 0;
  ensemble [elibre++] = -1;
  transition (elibre - 2, a.premier);

  /* Affichage de l'automate */
  
  for (i = 0; i < glibre; i++) {
    p = graphe [i].source;
    q = graphe [i].but;
    printf ("%3d -> %3d (%c)\n", p, q, lettre [q]);
  }
  printf ("\n");
}

attribut feuille (char id) {
  attribut a;
  
  if (elibre + 4 > EMAX)
    debordement ("ensemble");
  
  a.vide = 0;
  a.premier = elibre;
  ensemble [elibre++] = position;
  ensemble [elibre++] = -1;
  a.dernier = elibre;
  ensemble [elibre++] = position;
  ensemble [elibre++] = -1;
  lettre [position] = id;
  
  position++;
  return a;
}

attribut noeud (char operateur, attribut gauche, attribut droite) {
  attribut a;
  
  switch (operateur) {
    case '|':
      a.vide = gauche.vide || droite.vide;
      a.premier = fusion (gauche.premier, droite.premier);
      a.dernier = fusion (gauche.dernier, droite.dernier);
      break;
      
    case '.':
      a.vide = gauche.vide && droite.vide;
      if (gauche.vide)
        a.premier = fusion (gauche.premier, droite.premier);
      else
        a.premier = gauche.premier;
      if (droite.vide)
        a.dernier = fusion (gauche.dernier, droite.dernier);
      else
        a.dernier = droite.dernier;
      break;
  }
  return a;
}

void transition (int source, int but) {
  int i, j, p, q;
  for (i = source; (p = ensemble [i]) >= 0; i++)
    for (j = but; (q = ensemble [j]) >= 0; j++)
      if (glibre < GMAX) {
        graphe [glibre].source = p;
        graphe [glibre].but = q;
        glibre++;
      }
      else
        debordement ("graphe");
}

Jeu d'essais

verlaine% automate
a
Premier :  1
Dernier :  1

  0 ->   1 (a)

a*b
Premier :  1  2
Dernier :  2

  1 ->   1 (a)
  1 ->   2 (b)
  0 ->   1 (a)
  0 ->   2 (b)

a+b
Premier :  1
Dernier :  2

  1 ->   1 (a)
  1 ->   2 (b)
  0 ->   1 (a)

a?b
Premier :  1  2
Dernier :  2

  1 ->   2 (b)
  0 ->   1 (a)
  0 ->   2 (b)

(b|a*x)*a+b
Premier :  1  2  3  4
Dernier :  5

  2 ->   2 (a)
  2 ->   3 (x)
  1 ->   1 (b)
  1 ->   2 (a)
  1 ->   3 (x)
  3 ->   1 (b)
  3 ->   2 (a)
  3 ->   3 (x)
  4 ->   4 (a)
  1 ->   4 (a)
  3 ->   4 (a)
  4 ->   5 (b)
  0 ->   1 (b)
  0 ->   2 (a)
  0 ->   3 (x)
  0 ->   4 (a)

*a
parse error
verlaine%