automaton.c

Go to the documentation of this file.
00001 /* Eliot                                                                     */
00002 /* Copyright (C) 2005  Antoine Fraboulet                                     */
00003 /*                                                                           */
00004 /* This file is part of Eliot.                                               */
00005 /*                                                                           */
00006 /* Eliot is free software; you can redistribute it and/or modify             */
00007 /* it under the terms of the GNU General Public License as published by      */
00008 /* the Free Software Foundation; either version 2 of the License, or         */
00009 /* (at your option) any later version.                                       */
00010 /*                                                                           */
00011 /* Eliot is distributed in the hope that it will be useful,                  */
00012 /* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
00013 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */
00014 /* GNU General Public License for more details.                              */
00015 /*                                                                           */
00016 /* You should have received a copy of the GNU General Public License         */
00017 /* along with this program; if not, write to the Free Software               */
00018 /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00019 
00020 /**
00021  *  \file   automaton.c
00022  *  \brief  (Non)Deterministic Finite Automaton for Regexp
00023  *  \author Antoine Fraboulet
00024  *  \date   2005
00025  */
00026 
00027 #include "config.h"
00028 #include <assert.h>
00029 #include <string.h>
00030 #include <stdlib.h>
00031 #include <stdio.h>
00032 #include <sys/types.h>
00033 #ifdef HAVE_SYS_WAIT_H
00034 #   include <sys/wait.h>
00035 #endif
00036 #include <unistd.h>
00037 
00038 #include "dic.h"
00039 #include "regexp.h"
00040 #include "alist.h"
00041 #include "automaton.h"
00042 
00043 #ifdef DEBUG_AUTOMATON
00044 #define DMSG(a) a
00045 #else
00046 #define DMSG(a)
00047 #endif
00048 
00049 #define MAX_TRANSITION_LETTERS 256
00050 
00051 typedef struct automaton_state_t *astate;
00052 typedef struct Automaton_t       *Automaton;
00053 
00054 /* ************************************************** *
00055    exported functions for static automata
00056  * ************************************************** */
00057 
00058 automaton automaton_build          (int init_state, int *ptl, int *PS, struct search_RegE_list_t *list);
00059 void      automaton_delete         (automaton a);
00060 int       automaton_get_nstate     (automaton a);
00061 int       automaton_get_init       (automaton a);
00062 int       automaton_get_accept     (automaton a, int state);
00063 int       automaton_get_next_state (automaton a, int start, char l);
00064 void      automaton_dump           (automaton a, char* filename);
00065 
00066 
00067 /* ************************************************** *
00068    static functions for dynamic automata
00069  * ************************************************** */
00070 
00071 static Automaton s_automaton_create         ();
00072 static void      s_automaton_delete         (Automaton a);
00073 
00074 static alist     s_automaton_id_create      (int id);
00075 static char*     s_automaton_id_to_str      (alist id);
00076 
00077 static astate    s_automaton_state_create   (alist id);
00078 
00079 static void      s_automaton_add_state      (Automaton a, astate s);
00080 static astate    s_automaton_get_state      (Automaton a, alist id);
00081 
00082 static Automaton s_automaton_PS_to_NFA      (int init_state, int *ptl, int *PS);
00083 static Automaton s_automaton_NFA_to_DFA     (Automaton a, struct search_RegE_list_t *list);
00084 static automaton s_automaton_finalize       (Automaton a);
00085 #ifdef DEBUG_AUTOMATON
00086 static void      s_automaton_dump           (Automaton a, char* filename);
00087 #endif
00088 
00089 /* ************************************************** *
00090    data types
00091  * ************************************************** */
00092 
00093 struct automaton_state_t {
00094   alist    id;                     // alist of int
00095   int      accept;
00096   int      id_static;
00097   astate   next[MAX_TRANSITION_LETTERS];
00098 };
00099 
00100 struct Automaton_t {
00101   int      nstates;
00102   astate   init_state;
00103   alist    states;                 // alist of alist of int
00104 };
00105 
00106 struct automaton_t {
00107   int   nstates;
00108   int   init;
00109   int  *accept;
00110   int **trans;
00111 };
00112 
00113 /* ************************************************** *
00114    exported functions for static automata
00115  * ************************************************** */
00116 
00117 automaton
00118 automaton_build(int init_state, int *ptl, int *PS, struct search_RegE_list_t *list)
00119 {
00120   Automaton nfa,dfa;
00121   automaton final;
00122 
00123   nfa = s_automaton_PS_to_NFA(init_state,ptl,PS);
00124   DMSG(printf("\n non deterministic automaton OK \n\n"));
00125   DMSG(s_automaton_dump(nfa,"auto_nfa"));
00126 
00127   dfa = s_automaton_NFA_to_DFA(nfa, list);
00128   DMSG(printf("\n deterministic automaton OK \n\n"));
00129   DMSG(s_automaton_dump(dfa,"auto_dfa"));
00130 
00131   final = s_automaton_finalize(dfa);
00132   DMSG(printf("\n final automaton OK \n\n"));
00133   DMSG(automaton_dump(final,"auto_fin"));
00134 
00135   s_automaton_delete(nfa);
00136   s_automaton_delete(dfa);
00137   return final;
00138 }
00139 
00140 void
00141 automaton_delete(automaton a)
00142 {
00143   int i;
00144   free(a->accept);
00145   for(i=0; i <= a->nstates; i++)
00146     free(a->trans[i]);
00147   free(a->trans);
00148   free(a);
00149 }
00150 
00151 inline int
00152 automaton_get_nstates(automaton a)
00153 {
00154   return a->nstates;
00155 }
00156 
00157 inline int
00158 automaton_get_init(automaton a)
00159 {
00160   return a->init;
00161 }
00162 
00163 inline int
00164 automaton_get_accept(automaton a, int state)
00165 {
00166   return a->accept[state];
00167 }
00168 
00169 inline int
00170 automaton_get_next_state(automaton a, int state, char l)
00171 {
00172   return a->trans[state][(int)l];
00173 }
00174 
00175 void
00176 automaton_dump(automaton a, char* filename)
00177 {
00178   int i,l;
00179   FILE* f;
00180 #ifdef HAVE_SYS_WAIT_H
00181   pid_t   pid;
00182 #endif
00183 
00184   if (a == NULL)
00185     return ;
00186   f=fopen(filename,"w");
00187   fprintf(f,"digraph automaton {\n");
00188   for(i=1; i<=a->nstates; i++)
00189     {
00190       fprintf(f,"\t%d [label = \"%d\"",i,i);
00191       if (i == a->init)
00192         fprintf(f,", style = filled, color=lightgrey");
00193       if (a->accept[i])
00194         fprintf(f,", shape = doublecircle");
00195       fprintf(f,"];\n");
00196     }
00197   fprintf(f,"\n");
00198   for(i=1; i<=a->nstates; i++)
00199     for(l=0; l < MAX_TRANSITION_LETTERS; l++)
00200       if (a->trans[i][l])
00201         {
00202           fprintf(f,"\t%d -> %d [label = \"",i,a->trans[i][l]);
00203           regexp_print_letter(f,l);
00204           fprintf(f,"\"];\n");
00205         }
00206   fprintf(f,"fontsize=20;\n");
00207   fprintf(f,"}\n");
00208   fclose(f);
00209 
00210 #ifdef HAVE_SYS_WAIT_H
00211   pid = fork ();
00212   if (pid > 0) {
00213     wait(NULL);
00214   } else if (pid == 0) {
00215     execlp("dotty","dotty",filename,NULL);
00216     printf("exec dotty failed\n");
00217     exit(1);
00218   }
00219 #endif
00220 }
00221 
00222 /* ************************************************** *
00223  * ************************************************** *
00224  * ************************************************** */
00225 
00226 void
00227 state_delete_fun(void* ps)
00228 {
00229   astate s = ps;
00230   alist_delete(s->id);
00231   free(s);
00232 }
00233 
00234 static Automaton
00235 s_automaton_create()
00236 {
00237   Automaton a;
00238   a = (Automaton)malloc(sizeof(struct Automaton_t));
00239   a->nstates      = 0;
00240   a->init_state   = NULL;
00241   a->states       = alist_create();
00242   alist_set_delete(a->states,state_delete_fun);
00243   return a;
00244 }
00245 
00246 
00247 static void
00248 s_automaton_delete(Automaton a)
00249 {
00250   alist_delete(a->states);
00251   free(a);
00252 }
00253 
00254 static alist
00255 s_automaton_id_create(int id)
00256 {
00257   alist a = alist_create();
00258   alist_add(a,(void*)id);
00259   return a;
00260 }
00261 
00262 static char* s_automaton_id_to_str(alist id)
00263 {
00264   static char s[250];
00265   memset(s,0,sizeof(s));
00266   alist_elt ptr;
00267   for(ptr = alist_get_first(id); ptr ; ptr = alist_get_next(id,ptr))
00268     {
00269       char tmp[50];
00270       sprintf(tmp,"%d ",(int)alist_elt_get_value(ptr));
00271       strcat(s,tmp);
00272     }
00273   return s;
00274 }
00275 
00276 static astate
00277 s_automaton_state_create(alist id)
00278 {
00279   astate s;
00280   s = (astate)malloc(sizeof(struct automaton_state_t));
00281   s->id      = id;
00282   s->accept  = 0;
00283   memset(s->next,0,sizeof(astate)*MAX_TRANSITION_LETTERS);
00284   DMSG(printf("** state %s creation\n",s_automaton_id_to_str(id)));
00285   return s;
00286 }
00287 
00288 static void
00289 s_automaton_add_state(Automaton a, astate s)
00290 {
00291   a->nstates ++;
00292   alist_add(a->states,(void*)s);
00293   DMSG(printf("** state %s added to automaton\n",s_automaton_id_to_str(s->id)));
00294 }
00295 
00296 static astate
00297 s_automaton_get_state(Automaton a, alist id)
00298 {
00299   astate s;
00300   alist_elt ptr;
00301   for(ptr = alist_get_first(a->states) ;  ptr ; ptr = alist_get_next(a->states,ptr))
00302     {
00303       s = alist_elt_get_value(ptr);
00304       if (alist_equal(s->id,id))
00305         {
00306           //DMSG(printf("** get state %s ok\n",s_automaton_id_to_str(s->id)));
00307           return s;
00308         }
00309     }
00310   return NULL;
00311 }
00312 
00313 /* ************************************************** *
00314  * ************************************************** *
00315  * ************************************************** */
00316 
00317 Automaton
00318 s_automaton_PS_to_NFA(int init_state_id, int *ptl, int *PS)
00319 {
00320   int p;
00321   int maxpos = PS[0];
00322   Automaton nfa = NULL;
00323   alist temp_id;
00324   alist_elt ptr;
00325   astate temp_state,current_state;
00326   alist L;
00327   char used_letter[MAX_TRANSITION_LETTERS];
00328 
00329   nfa = s_automaton_create();
00330   L   = alist_create();
00331 
00332   /* 1: init_state = root->PP */
00333   temp_id          = s_automaton_id_create(init_state_id);
00334   temp_state       = s_automaton_state_create(temp_id);
00335   nfa->init_state  = temp_state;
00336   s_automaton_add_state(nfa,temp_state);
00337   alist_add(L,temp_state);
00338   /* 2: while \exist state \in state_list */
00339   while (! alist_is_empty(L))
00340     {
00341       current_state = (astate)alist_pop_first_value(L);
00342       DMSG(printf("** current state = %s\n",s_automaton_id_to_str(current_state->id)));
00343       memset(used_letter,0,sizeof(used_letter));
00344       /* 3: \foreach l in \sigma | l \neq # */
00345       for(p=1; p < maxpos; p++)
00346         {
00347           int current_letter = ptl[p];
00348           if (used_letter[current_letter] == 0)
00349             {
00350               /* 4: int set = \cup { PS(pos) | pos \in state \wedge pos == l } */
00351               int pos, ens = 0;
00352               for(pos = 1; pos <= maxpos; pos++)
00353                 {
00354                   if (ptl[pos] == current_letter &&
00355                       (int)alist_elt_get_value(alist_get_first(current_state->id)) & (1 << (pos - 1)))
00356                     ens |= PS[pos];
00357                 }
00358               /* 5: transition from current_state to temp_state */
00359               if (ens)
00360                 {
00361                   temp_id    = s_automaton_id_create(ens);
00362                   temp_state = s_automaton_get_state(nfa,temp_id);
00363                   if (temp_state == NULL)
00364                     {
00365                       temp_state = s_automaton_state_create(temp_id);
00366                       s_automaton_add_state     (nfa,temp_state);
00367                       current_state->next[current_letter] = temp_state;
00368                       alist_add(L,temp_state);
00369                     }
00370                   else
00371                     {
00372                       alist_delete(temp_id);
00373                       current_state->next[current_letter] = temp_state;
00374                     }
00375                 }
00376               used_letter[current_letter] = 1;
00377             }
00378         }
00379     }
00380 
00381   alist_delete(L);
00382 
00383   for(ptr = alist_get_first(nfa->states); ptr ; ptr = alist_get_next(nfa->states,ptr))
00384     {
00385       astate s = (astate)alist_elt_get_value(ptr);
00386       if ((int)alist_elt_get_value(alist_get_first(s->id)) & (1 << (maxpos - 1)))
00387         s->accept = 1;
00388     }
00389 
00390   return nfa;
00391 }
00392 
00393 /* ************************************************** *
00394  * ************************************************** *
00395  * ************************************************** */
00396 
00397 static alist
00398 s_automaton_successor(alist S, int letter, Automaton nfa, struct search_RegE_list_t *list)
00399 {
00400   alist R,r;
00401   alist_elt ptr;
00402   R = alist_create();                                       /* R = \empty */
00403                                                             /* \forall y \in S */
00404   for(ptr = alist_get_first(S); ptr ; ptr = alist_get_next(S,ptr))
00405     {
00406       int i;
00407       alist t, Ry; astate y,z;
00408 
00409       i = (int)alist_elt_get_value(ptr);
00410       t = s_automaton_id_create(i);
00411       assert(y = s_automaton_get_state(nfa,t));
00412       alist_delete(t);
00413 
00414       Ry = alist_create();                                 /* Ry = \empty             */
00415 
00416       if ((z = y->next[letter]) != NULL)                   /* \delta (y,z) = l        */
00417         {
00418           r = s_automaton_successor(z->id,RE_EPSILON,nfa, list);
00419           alist_insert(Ry,r);
00420           alist_delete(r);
00421           alist_insert(Ry,z->id);                          /* Ry = Ry \cup succ(z)    */
00422         }
00423 
00424       /* \epsilon transition from start node */
00425       if ((z = y->next[RE_EPSILON]) != NULL)               /* \delta (y,z) = \epsilon */
00426         {
00427           r = s_automaton_successor(z->id,letter,nfa, list);
00428           alist_insert(Ry,r);                              /* Ry = Ry \cup succ(z)    */
00429           alist_delete(r);
00430         }
00431 
00432       if (letter < RE_FINAL_TOK)
00433         {
00434           for(i = 0 ; i < DIC_SEARCH_REGE_LIST ; i++)
00435             if (list->valid[i])
00436               {
00437                 if (list->letters[i][letter] && (z = y->next[(int)list->symbl[i]]) != NULL)
00438                   {
00439                     DMSG(printf("*** letter "));
00440                     DMSG(regexp_print_letter(stdout,letter));
00441                     DMSG(printf("is in "));
00442                     DMSG(regexp_print_letter(stdout,i));
00443 
00444                     r = s_automaton_successor(z->id,RE_EPSILON,nfa, list);
00445                     alist_insert(Ry,r);
00446                     alist_delete(r);
00447                     alist_insert(Ry,z->id);
00448                   }
00449               }
00450         }
00451 
00452 #if 0
00453       if (alist_is_empty(Ry))                              /* Ry = \empty             */
00454         return Ry;
00455 #endif
00456 
00457       alist_insert(R,Ry);                                  /* R = R \cup Ry           */
00458       alist_delete(Ry);
00459     }
00460 
00461   return R;
00462 }
00463 
00464 static void
00465 s_automaton_node_set_accept(astate s, Automaton nfa)
00466 {
00467   void* idx;
00468   alist_elt ptr;
00469 
00470   DMSG(printf("=== setting accept for node (%s) :",s_automaton_id_to_str(s->id)));
00471   for(ptr = alist_get_first(nfa->states) ; ptr ; ptr = alist_get_next(nfa->states,ptr))
00472     {
00473       astate ns = (astate)alist_elt_get_value(ptr);
00474       idx = alist_elt_get_value(alist_get_first(ns->id));
00475       DMSG(printf("%s ",s_automaton_id_to_str(ns->id)));
00476       if (ns->accept && alist_is_in(s->id,idx))
00477         {
00478           DMSG(printf("(ok) "));
00479           s->accept = 1;
00480         }
00481     }
00482   DMSG(printf("\n"));
00483 }
00484 
00485 static Automaton
00486 s_automaton_NFA_to_DFA(Automaton nfa, struct search_RegE_list_t *list)
00487 {
00488   Automaton dfa = NULL;
00489   alist temp_id;
00490   alist_elt ptr;
00491   astate temp_state, current_state;
00492   alist L;
00493   int letter;
00494 
00495   dfa = s_automaton_create();
00496   L   = alist_create();
00497 
00498   temp_id         = alist_clone(nfa->init_state->id);
00499   temp_state      = s_automaton_state_create(temp_id);
00500   dfa->init_state = temp_state;
00501   s_automaton_add_state(dfa,temp_state);
00502   alist_add(L,temp_state);
00503   while (! alist_is_empty(L))
00504     {
00505       current_state = (astate)alist_pop_first_value(L);
00506       DMSG(printf("** current state = %s\n",s_automaton_id_to_str(current_state->id)));
00507       for(letter = 1; letter < DIC_LETTERS; letter++)
00508         {
00509           //      DMSG(printf("*** start successor of %s\n",s_automaton_id_to_str(current_state->id)));
00510 
00511           temp_id = s_automaton_successor(current_state->id,letter,nfa,list);
00512 
00513           if (! alist_is_empty(temp_id))
00514             {
00515 
00516               DMSG(printf("*** successor of %s for ",s_automaton_id_to_str(current_state->id)));
00517               DMSG(regexp_print_letter(stdout,letter));
00518               DMSG(printf(" = %s\n", s_automaton_id_to_str(temp_id)));
00519 
00520               temp_state = s_automaton_get_state(dfa,temp_id);
00521 
00522               //          DMSG(printf("*** automaton get state -%s- ok\n",s_automaton_id_to_str(temp_id)));
00523 
00524               if (temp_state == NULL)
00525                 {
00526                   temp_state = s_automaton_state_create(temp_id);
00527                   s_automaton_add_state(dfa,temp_state);
00528                   current_state->next[letter] = temp_state;
00529                   alist_add(L,temp_state);
00530                 }
00531               else
00532                 {
00533                   alist_delete(temp_id);
00534                   current_state->next[letter] = temp_state;
00535                 }
00536             }
00537           else
00538             {
00539               alist_delete(temp_id);
00540             }
00541         }
00542     }
00543 
00544   for(ptr = alist_get_first(dfa->states) ; ptr ; ptr = alist_get_next(dfa->states,ptr))
00545     {
00546       astate s = (astate)alist_elt_get_value(ptr);
00547       s_automaton_node_set_accept(s,nfa);
00548     }
00549 
00550   alist_delete(L);
00551   return dfa;
00552 }
00553 
00554 /* ************************************************** *
00555  * ************************************************** *
00556  * ************************************************** */
00557 
00558 static automaton
00559 s_automaton_finalize(Automaton a)
00560 {
00561   int i,l;
00562   automaton fa = NULL;
00563   alist_elt ptr;
00564   astate s;
00565 
00566   if (a == NULL)
00567     return NULL;
00568 
00569   /* creation */
00570   fa = (automaton)malloc(sizeof(struct automaton_t));
00571   fa->nstates = a->nstates;
00572   fa->accept  = (int*) malloc((fa->nstates + 1)*sizeof(int));
00573   memset(fa->accept,0,(fa->nstates + 1)*sizeof(int));
00574   fa->trans   = (int**)malloc((fa->nstates + 1)*sizeof(int*));
00575   for(i=0; i <= fa->nstates; i++)
00576     {
00577       fa->trans[i] = (int*)malloc(MAX_TRANSITION_LETTERS * sizeof(int));
00578       memset(fa->trans[i],0,MAX_TRANSITION_LETTERS * sizeof(int));
00579     }
00580 
00581   /* create new id for states */
00582   for(i = 1 , ptr = alist_get_first(a->states); ptr ; ptr = alist_get_next(a->states,ptr), i++)
00583     {
00584       s = (astate)alist_elt_get_value(ptr);
00585       s->id_static = i;
00586     }
00587 
00588   /* build new automaton */
00589   for(ptr = alist_get_first(a->states); ptr ; ptr = alist_get_next(a->states,ptr))
00590     {
00591       s = (astate)alist_elt_get_value(ptr);
00592       i = s->id_static;
00593 
00594       if (s == a->init_state)
00595         fa->init = i;
00596       if (s->accept == 1)
00597         fa->accept[i] = 1;
00598 
00599       for(l=0; l < MAX_TRANSITION_LETTERS; l++)
00600         if (s->next[l])
00601           fa->trans[i][l] = s->next[l]->id_static;
00602     }
00603 
00604   return fa;
00605 }
00606 
00607 
00608 /* ************************************************** *
00609  * ************************************************** *
00610  * ************************************************** */
00611 
00612 static void
00613 s_automaton_print_nodes(FILE* f, Automaton a)
00614 {
00615   char * sid;
00616   astate s;
00617   alist_elt ptr;
00618   for(ptr = alist_get_first(a->states) ; ptr != NULL ; ptr = alist_get_next(a->states,ptr))
00619     {
00620       s = alist_elt_get_value(ptr);
00621       sid = s_automaton_id_to_str(s->id);
00622       fprintf(f,"\t\"%s\" [label = \"%s\"",sid,sid);
00623       if (s == a->init_state)
00624             {
00625               fprintf(f,", style = filled, color=lightgrey");
00626             }
00627       if (s->accept)
00628         {
00629           fprintf(f,", shape = doublecircle");
00630         }
00631       fprintf(f,"];\n");
00632     }
00633   fprintf(f,"\n");
00634 }
00635 
00636 static void
00637 s_automaton_print_edges(FILE* f, Automaton a)
00638 {
00639   int letter;
00640   char * sid;
00641   astate s;
00642   alist_elt ptr;
00643   for(ptr = alist_get_first(a->states) ; ptr != NULL ; ptr = alist_get_next(a->states,ptr))
00644     {
00645       s = (astate)alist_elt_get_value(ptr);
00646       for(letter=0; letter < 255; letter++)
00647         {
00648           if (s->next[letter])
00649             {
00650               sid = s_automaton_id_to_str(s->id);
00651               fprintf(f,"\t\"%s\" -> ",sid);
00652               sid = s_automaton_id_to_str(s->next[letter]->id);
00653               fprintf(f,"\"%s\" [label = \"",sid);
00654               regexp_print_letter(f,letter);
00655               fprintf(f,"\"];\n");
00656             }
00657         }
00658     }
00659 }
00660 
00661 static void
00662 s_automaton_dump(Automaton a, char* filename)
00663 {
00664   FILE* f;
00665 #ifdef HAVE_SYS_WAIT_H
00666   pid_t   pid;
00667 #endif
00668   if (a == NULL)
00669     return;
00670   f=fopen(filename,"w");
00671   fprintf(f,"digraph automaton {\n");
00672   s_automaton_print_nodes(f,a);
00673   s_automaton_print_edges(f,a);
00674   fprintf(f,"fontsize=20;\n");
00675   fprintf(f,"}\n");
00676   fclose(f);
00677 
00678 #ifdef HAVE_SYS_WAIT_H
00679   pid = fork ();
00680   if (pid > 0) {
00681     wait(NULL);
00682   } else if (pid == 0) {
00683     execlp("dotty","dotty",filename,NULL);
00684     printf("exec dotty failed\n");
00685     exit(1);
00686   }
00687 #endif
00688 }
00689 
00690 /* ************************************************** *
00691  * ************************************************** *
00692  * ************************************************** */
00693 

Generated on Thu Dec 29 02:01:14 2005 for Eliot by  doxygen 1.4.5