/***************************/ /* Implémentation des NFAs */ /***************************/ #include "nfa.h" #include "error.h" #include "graphs.h" #include "type_dequeue.h" #include #include #include #define CHECK(ptr) \ if (ptr == NULL) { \ ERROR("malloc failed"); \ exit(EXIT_FAILURE); \ } static char *uint_to_chars(const uint i) { char *str = malloc(12 * sizeof(char)); // 12 car un uint peut avoir 10 chiffres + le '\0' (4 294 967 295) + jsp CHECK(str); sprintf(str, "%u", i); return str; } /***************************************************************/ /* Affichage des noms si ce sont des pointeurs sur des entiers */ /***************************************************************/ void nfa_print_letter(const nfa *thenfa, const uint num_letter, FILE *out) { printf("nfa_print_letter\n"); fprintf(out, "%c", thenfa->alpha_names[num_letter]); } char *nfa_copy_alpha(const nfa *thenfa) { printf("nfa_copy_alpha\n"); char *copy = malloc((thenfa->trans->size_alpha) * sizeof(char)); CHECK(copy); for (uint i = 0; i < thenfa->trans->size_alpha; i++) { copy[i] = thenfa->alpha_names[i]; } return copy; } void nfa_print_state(const nfa *thenfa, const uint numstate, FILE *out) { printf("nfa_print_state\n"); if (thenfa->state_names == NULL) { fprintf(out, "%u", numstate); } else { fprintf(out, "%s", thenfa->state_names[numstate]); } } void nfa_reset_state_names(nfa *thenfa) { printf("nfa_reset_state_names\n"); if (thenfa->state_names != NULL) { for (uint i = 0; i < thenfa->trans->size_alpha; i++) { if (thenfa->state_names[i] != NULL) { free(thenfa->state_names[i]); } } free(thenfa->state_names); thenfa->state_names = NULL; } } char *nfa_copy_one_name(const nfa *thenfa, const uint thestate) { printf("nfa_copy_one_name\n"); if (thenfa->state_names == NULL) { return uint_to_chars(thestate); } const size_t len = strlen(thenfa->state_names[thestate]) + 1; //+1 car strlen ne compte pas le '\0' char *copy = malloc(len * sizeof(char)); CHECK(copy); strncpy(copy, thenfa->state_names[thestate], len); return copy; } // ReSharper disable once CppParameterMayBeConstPtrOrRef char **nfa_copy_all_names(nfa *thenfa) { printf("nfa_copy_all_names\n"); // Vérifier si le NFA utilise des noms pour les états if (thenfa->state_names == NULL) { return NULL; } // Allouer de la mémoire pour le tableau de noms char **copy_names = malloc(thenfa->trans->size_graph * sizeof(char *)); CHECK(copy_names); // Copier chaque nom d'état for (uint i = 0; i < thenfa->trans->size_graph; i++) { copy_names[i] = nfa_copy_one_name(thenfa, i); } return copy_names; } /*********************************/ /* Initialisation et suppression */ /*********************************/ nfa *create_emptylang(void) { printf("create_emptylang\n"); nfa *empty = malloc(sizeof(nfa)); CHECK(empty); empty->trans = create_lgraph_noedges(0, 0); empty->initials = create_dequeue(); empty->finals = create_dequeue(); empty->alpha_names = malloc(sizeof(char)); CHECK(empty->alpha_names); empty->alpha_names[0] = '\0'; empty->state_names = NULL; return empty; } nfa *create_sing_epsilon(void) { printf("create_sing_epsilon\n"); nfa *epsilon = malloc(sizeof(nfa)); CHECK(epsilon); epsilon->trans = create_lgraph_noedges(1, 0); epsilon->initials = create_dequeue(); epsilon->finals = create_dequeue(); lefins_dequeue(0, epsilon->initials); lefins_dequeue(0, epsilon->finals); epsilon->alpha_names = malloc(sizeof(char)); CHECK(epsilon->alpha_names); epsilon->alpha_names[0] = '\0'; epsilon->state_names = malloc(sizeof(char *)); CHECK(epsilon->state_names); epsilon->state_names[0] = "ε"; return epsilon; } nfa *create_sing_letter(const char theletter) { printf("create_sing_letter\n"); // Créer le graphe des transitions avec 2 états et 1 lettre dans l'alphabet lgraph *trans = create_lgraph_noedges(2, 1); // Ajouter une transition de l'état 0 à l'état 1 avec la lettre theletter dequeue *transition = create_dequeue(); rigins_dequeue(1, transition); trans->edges[0][0] = transition; // Créer la liste des états initiaux (état 0) dequeue *initials = create_dequeue(); rigins_dequeue(0, initials); // Créer la liste des états finaux (état 1) dequeue *finals = create_dequeue(); rigins_dequeue(1, finals); // Allouer de la mémoire pour le nom de la lettre char *alpha_names = malloc(sizeof(char)); CHECK(alpha_names); alpha_names[0] = theletter; // Créer le NFA nfa *thenfa = malloc(sizeof(nfa)); thenfa->trans = trans; thenfa->initials = initials; thenfa->finals = finals; thenfa->alpha_names = alpha_names; thenfa->state_names = NULL; // Pas de noms d'états return thenfa; } void nfa_delete(nfa *thenfa) { printf("nfa_delete\n"); // ne free pas le pointeur ! nfa_reset_state_names(thenfa); delete_lgraph(thenfa->trans); delete_dequeue(thenfa->initials); delete_dequeue(thenfa->finals); free(thenfa->alpha_names); } void nfa_overwrite(nfa *nfa1, nfa *nfa2) { printf("nfa_overwrite\n"); nfa_delete(nfa1); *nfa1 = *nfa2; free(nfa2); } /***********************************/ /* Opérations simples sur les NFAs */ /***********************************/ nfa *nfa_copy(nfa *thenfa) { printf("nfa_copy\n"); nfa *copy = malloc(sizeof(nfa)); CHECK(copy); copy->initials = create_dequeue(); copy->finals = create_dequeue(); copy_dequeue_right(copy->initials, thenfa->initials, 0); copy_dequeue_right(copy->finals, thenfa->finals, 0); copy->alpha_names = nfa_copy_alpha(thenfa); copy->state_names = nfa_copy_all_names(thenfa); copy->trans = create_lgraph_noedges(thenfa->trans->size_graph, thenfa->trans->size_alpha); for (uint i = 0; i < thenfa->trans->size_graph; i++) { for (uint j = 0; j < thenfa->trans->size_alpha; j++) { dequeue *edges = create_dequeue(); copy_dequeue_right(edges, thenfa->trans->edges[i][j], 0); copy->trans->edges[i][j] = edges; } } return copy; } nfa *nfa_copy_exalpha(nfa *thenfa, const char *letters, const uchar tabsize) { nfa *copy = nfa_copy(thenfa); CHECK(copy); char *alpha = malloc(sizeof(char) * (copy->trans->size_alpha + tabsize)); CHECK(alpha); for (uint l = 0; l < copy->trans->size_alpha; l++) { alpha[l] = copy->alpha_names[l]; } for (uint l = 0; l < tabsize; l++) { alpha[l + copy->trans->size_alpha] = letters[l]; } free(copy->alpha_names); copy->alpha_names = alpha; return copy; } // Union disjointe de deux nfas // ReSharper disable once CppParameterMayBeConstPtrOrRef nfa *nfa_union(nfa *nfa1, nfa *nfa2) { printf("nfa_union\n"); // nfa1 U nfa2 = nfa1 + nfa2 - (nfa1 ∩ nfa2) nfa *nfa_uni = nfa_copy_exalpha(nfa1, nfa2->alpha_names, nfa2->trans->size_alpha); // On remplace les noms d'états nfa_reset_state_names(nfa_uni); // On unie les états initiaux copy_dequeue_right(nfa_uni->initials, nfa2->initials, nfa1->trans->size_graph); // On unie les états finaux copy_dequeue_right(nfa_uni->finals, nfa2->finals, nfa1->trans->size_graph); // on crée le graphe lgraph *g = create_lgraph_noedges(nfa1->trans->size_graph + nfa2->trans->size_graph, nfa1->trans->size_alpha + nfa2->trans->size_alpha); // on récupère le premier graphe for (uint sommet = 0; sommet < nfa1->trans->size_graph; sommet++) { for (uint lettre = 0; lettre < nfa1->trans->size_alpha; lettre++) { copy_dequeue_right(g->edges[sommet][lettre], nfa1->trans->edges[sommet][lettre], 0); } } // on récupère le second graphe const uint d1 = nfa1->trans->size_graph; const uint d2 = nfa1->trans->size_alpha; for (uint sommet = 0; sommet < nfa2->trans->size_graph; sommet++) { for (uint lettre = 0; lettre < nfa2->trans->size_alpha; lettre++) { copy_dequeue_right(g->edges[sommet + d1][lettre + d2], nfa2->trans->edges[sommet][lettre], d1); } } delete_lgraph(nfa_uni->trans); nfa_uni->trans = g; return nfa_uni; } // ReSharper disable once CppParameterMayBeConstPtrOrRef nfa *nfa_concat(nfa *nfa1, nfa *nfa2) { printf("nfa_concat\n"); // nfa1 . nfa2 <=> ε -> nfa1 -> nfa2 -> ε nfa *nfa_concaten = nfa_copy_exalpha(nfa1, nfa2->alpha_names, nfa2->trans->size_alpha); // On remplace les noms d'états nfa_reset_state_names(nfa_concaten); // on corrige les états finaux delete_dequeue(nfa_concaten->finals); nfa_concaten->finals = create_dequeue(); copy_dequeue_right(nfa_concaten->finals, nfa2->finals, nfa1->trans->size_alpha); // on crée le graphe const uint cumul = size_dequeue(nfa2->initials); lgraph *g = create_lgraph_noedges( nfa1->trans->size_graph + nfa2->trans->size_graph - cumul, nfa1->trans->size_alpha + nfa2->trans->size_alpha); // on récupère le premier graphe for (uint sommet = 0; sommet < nfa1->trans->size_graph; sommet++) { for (uint lettre = 0; lettre < nfa1->trans->size_alpha; lettre++) { copy_dequeue_right(g->edges[sommet][lettre], nfa1->trans->edges[sommet][lettre], 0); } } // on récupère le second graphe uint d1 = nfa1->trans->size_alpha; uint d2 = nfa1->trans->size_graph; for (uint sommet = 0; sommet < nfa2->trans->size_graph; sommet++) { for (uint lettre = 0; lettre < nfa2->trans->size_alpha; lettre++) { copy_dequeue_right(g->edges[sommet + d2 - cumul][lettre + d1], nfa2->trans->edges[sommet][lettre], d2 - cumul); } } // on recupère les arrêtes de transition entre les deux graphes uint size = size_dequeue(nfa2->initials); uint size2 = size_dequeue(nfa1->finals); for (uint i = 0; i < size; i++) { const uint depart = lefread_dequeue(nfa2->initials, i); for (uint j = 0; i < size2; i++) { const uint arrivee = lefread_dequeue(nfa1->finals, j); for (uint lettre = 0; lettre < nfa2->trans->size_alpha; lettre++) { copy_dequeue_right(g->edges[arrivee][lettre + nfa1->trans->size_alpha], nfa2->trans->edges[depart][lettre], nfa1->trans->size_alpha); } } } delete_lgraph(nfa_concaten->trans); nfa_concaten->trans = g; return nfa_concaten; } // Étoile de Kleene d'un NFA nfa *nfa_star(nfa *thenfa) { printf("nfa_star\n"); // final pointe chaque initial et on rajoute un epsilon // ajoute les epsilon nfa *kleene = nfa_copy(thenfa); bool eps = false; // reconstitution du graphes avec un Epsilon lgraph *g = create_lgraph_noedges(kleene->trans->size_graph + 1, kleene->trans->size_alpha); for (uint sommet = 0; sommet < kleene->trans->size_graph; sommet++) { if (mem_dequeue(sommet, kleene->finals) && mem_dequeue(sommet, kleene->initials)) { // ε eps = true; continue; } // on récupère nos arrêtes for (uint lettre = 0; lettre < kleene->trans->size_alpha; lettre++) { dequeue *edges = kleene->trans->edges[sommet][lettre]; copy_dequeue_right(g->edges[sommet][lettre], edges, 0); // on ajoute les états initiaux à tout ce qui pointes sur un état // final uint size = size_dequeue(kleene->finals); for (uint s = 0; s < size; s++) { const uint sortie = lefread_dequeue(kleene->finals, s); // si on a une transition vers un état final, on ajoute les états // initiaux aux transitions if (mem_dequeue_sorted(sortie, edges)) { uint size2 = size_dequeue(kleene->initials); for (uint e = 0; e < size2; e++) { const uint entree = lefread_dequeue(kleene->initials, e); lefins_dequeue(entree, g->edges[sommet][lettre]); } sort_dequeue_norepeat(g->edges[sommet][lettre]); } } } } if (!eps) { insert_dequeue(kleene->initials, kleene->trans->size_graph); insert_dequeue(kleene->finals, kleene->trans->size_graph); } else { // on supprime l'état réservé pour l'epsilon g->size_graph--; for (uint lettre = 0; lettre < g->size_alpha; lettre++) { free(g->edges[g->size_graph][lettre]); } } delete_lgraph(kleene->trans); kleene->trans = g; return kleene; } // note les états accessibles depuis l'état donné // note les états depuis lesquels on peut atteindre un état final à partir // de l'état donné static bool trimer(nfa *thenfa, bool *reachable, bool *may_exit, // NOLINT(*-no-recursion) const uint sommet) { printf("trimer\n"); if (reachable[sommet]) { // on a déjà visité cet état return may_exit[sommet]; } reachable[sommet] = true; const lgraph *g = thenfa->trans; for (uint i = 0; i < g->size_alpha; i++) { dequeue *edges = g->edges[sommet][i]; const uint size = size_dequeue(edges); for (uint j = 0; j < size; j++) { const uint etat = lefread_dequeue(edges, j); if (trimer(thenfa, reachable, may_exit, etat)) { may_exit[sommet] = true; } } } return mem_dequeue_sorted(sommet, thenfa->finals); } // Élimination des états non-accessibles et non-co-accessibles // Le NFA produit n'est pas nécessairement complet nfa *nfa_trim(nfa *thenfa) { printf("nfa_trim\n"); nfa *trimmed = nfa_copy(thenfa); bool reachable[trimmed->trans->size_graph]; bool may_end[trimmed->trans->size_graph]; for (uint i = 0; i < trimmed->trans->size_graph; i++) { reachable[i] = false; may_end[i] = false; } // on marque les états accessibles uint size = size_dequeue(trimmed->initials); for (uint i = 0; i < size; i++) { const uint etat = lefread_dequeue(trimmed->initials, i); trimer(trimmed, reachable, may_end, etat); } // on compte les états accessibles int c = 0; for (uint i = 0; i < trimmed->trans->size_graph; i++) { if (reachable[i] && may_end[i]) { c++; } } // on crée un nouveau graphe lgraph *new_graph = create_lgraph_noedges(c, trimmed->trans->size_alpha); for (uint i = 0; i < new_graph->size_graph; i++) { for (uint j = 0; j < new_graph->size_alpha; j++) { // on récupère les arêtes accessibles dequeue *edges = new_graph->edges[i][j]; dequeue *old_edges = trimmed->trans->edges[i][j]; size = size_dequeue(old_edges); for (uint k = 0; k < size; k++) { const uint etat = lefread_dequeue(old_edges, k); if (reachable[etat] && may_end[etat]) { lefins_dequeue(etat, edges); } } } } // on remplace le graphe delete_lgraph(trimmed->trans); trimmed->trans = new_graph; // on supprime les états finaux non accessibles size = size_dequeue(trimmed->finals); for (uint i = 0; i < size; i++) { const uint etat = lefpull_dequeue(trimmed->finals); if (reachable[etat]) { rigins_dequeue(etat, trimmed->finals); } } // on supprime les états initiaux non accessibles size = size_dequeue(trimmed->initials); for (uint i = 0; i < size; i++) { const uint etat = lefpull_dequeue(trimmed->initials); if (reachable[etat]) { rigins_dequeue(etat, trimmed->initials); } } // on supprime les états non accessibles char **new_state_names = malloc(sizeof(char *) * c); CHECK(new_state_names); c = 0; for (uint i = 0; i < thenfa->trans->size_graph; i++) { if (reachable[i] && may_end[i]) { new_state_names[c] = nfa_copy_one_name(thenfa, i); c++; } } free(trimmed->state_names); trimmed->state_names = new_state_names; return trimmed; } // Élimination des états non-accessibles (modifie le NFA originel) void nfa_trim_mod(nfa *thenfa) { printf("nfa_trim_mod\n"); bool reachable[thenfa->trans->size_graph]; bool may_end[thenfa->trans->size_graph]; for (uint i = 0; i < thenfa->trans->size_graph; i++) { reachable[i] = false; may_end[i] = false; } // on marque les états accessibles uint size = size_dequeue(thenfa->initials); for (uint i = 0; i < size; i++) { const uint etat = lefread_dequeue(thenfa->initials, i); trimer(thenfa, reachable, may_end, etat); } // on compte les états accessibles int c = 0; for (uint i = 0; i < thenfa->trans->size_graph; i++) { if (reachable[i] && may_end[i]) { c++; } } // on crée un nouveau graphe lgraph *new_graph = create_lgraph_noedges(c, thenfa->trans->size_alpha); for (uint i = 0; i < new_graph->size_graph; i++) { for (uint j = 0; j < new_graph->size_alpha; j++) { // on récupère les arêtes accessibles dequeue *edges = new_graph->edges[i][j]; dequeue *old_edges = thenfa->trans->edges[i][j]; size = size_dequeue(old_edges); for (uint k = 0; k < size; k++) { const uint etat = lefread_dequeue(old_edges, k); if (reachable[etat] && may_end[etat]) { lefins_dequeue(etat, edges); } } } } // on remplace le graphe const uint saved_size = thenfa->trans->size_graph; // utile pour plus tard delete_lgraph(thenfa->trans); thenfa->trans = new_graph; // on supprime les états finaux non accessibles size = size_dequeue(thenfa->finals); for (uint i = 0; i < size; i++) { const uint etat = lefpull_dequeue(thenfa->finals); if (reachable[etat]) { rigins_dequeue(etat, thenfa->finals); } } // on supprime les états initiaux non accessibles size = size_dequeue(thenfa->initials); for (uint i = 0; i < size; i++) { const uint etat = lefpull_dequeue(thenfa->initials); if (reachable[etat]) { rigins_dequeue(etat, thenfa->initials); } } // on supprime les états non accessibles char **new_state_names = malloc(sizeof(char *) * c); CHECK(new_state_names); c = 0; for (uint i = 0; i < saved_size; i++) { if (reachable[i] && may_end[i]) { new_state_names[c] = nfa_copy_one_name(thenfa, i); c++; } } free(thenfa->state_names); thenfa->state_names = new_state_names; } // Miroir nfa *nfa_mirror(nfa *thenfa) { printf("nfa_mirror\n"); nfa *mirror = malloc(sizeof(nfa)); CHECK(mirror); // on inverse les états initiaux et finaux mirror->initials = create_dequeue(); copy_dequeue_right(mirror->initials, thenfa->finals, 0); mirror->finals = create_dequeue(); copy_dequeue_right(mirror->finals, thenfa->initials, 0); // on copie les alphabets mirror->alpha_names = nfa_copy_alpha(thenfa); // on copie les noms d'états mirror->state_names = nfa_copy_all_names(thenfa); // on prépare le graphe lgraph *g = create_lgraph_noedges(thenfa->trans->size_graph, thenfa->trans->size_alpha); for (uint sommet = 0; sommet < thenfa->trans->size_graph; sommet++) { for (uint lettre = 0; lettre < thenfa->trans->size_alpha; lettre++) { dequeue *edge = thenfa->trans->edges[sommet][lettre]; const uint size = size_dequeue(edge); for (uint i = 0; i < size; i++) { const uint etat = lefread_dequeue(edge, i); lefins_dequeue(sommet, g->edges[etat][lettre]); } } } mirror->trans = g; return mirror; } /************************/ /* Informations sur NFA */ /************************/ // ReSharper disable once CppParameterMayBeConstPtrOrRef int nfa_nb_trans(nfa *thenfa) { printf("nf_nb_trans\n"); int nb_trans = 0; for (uint sommet = 0; sommet < thenfa->trans->size_graph; sommet++) { for (uint lettre = 0; lettre < thenfa->trans->size_alpha; lettre++) { nb_trans += (int)size_dequeue(thenfa->trans->edges[sommet][lettre]); } } return nb_trans; } // ReSharper disable once CppParameterMayBeConstPtrOrRef bool nfa_is_det(nfa *thenfa) { printf("nfa_is_det\n"); printf("'ça va pas marcher mais chhhh...\n"); for (uint sommet = 0; sommet < thenfa->trans->size_graph; sommet++) { for (uint lettre = 0; lettre < thenfa->trans->size_alpha; lettre++) { if (size_dequeue(thenfa->trans->edges[sommet][lettre]) > 1) { return false; } } } return true; } bool nfa_is_comp(nfa *thenfa) { printf("nfa_is_comp\n"); for (uint sommet = 0; sommet < thenfa->trans->size_graph; sommet++) { for (uint lettre = 0; lettre < thenfa->trans->size_alpha; lettre++) { if (size_dequeue(thenfa->trans->edges[sommet][lettre]) < 1) { return false; } } } return true; } // ReSharper disable once CppParameterMayBeConstPtrOrRef bool nfa_is_empty(nfa *thenfa) { printf("nfa_is_empty\n"); return thenfa->trans->size_alpha == 0 && thenfa->trans->size_graph == 0; } bool nfa_accepts(nfa *thenfa, const char *theword) { printf("nfa_accepts\n"); dequeue *runs = nfa_compute_runs(thenfa, theword); const uint size = size_dequeue(runs); for (uint i = 0; i < size; i++) { const uint etat = lefread_dequeue(runs, i); if (mem_dequeue_sorted(etat, thenfa->finals)) { delete_dequeue(runs); return true; } } delete_dequeue(runs); return false; } // Calcule les états qui sont atteints par un mot dans un NFA. // ReSharper disable once CppParameterMayBeConstPtrOrRef dequeue *nfa_compute_runs(nfa *thenfa, const char *theword) { printf("nfa_compute_runs\n"); dequeue *runs = create_dequeue(); uint indice = 0; uint size = size_dequeue(thenfa->initials); while (indice < strlen(theword)) { dequeue *new_runs = create_dequeue(); for (uint i = 0; i < size; i++) { const uint etat = lefread_dequeue(thenfa->initials, i); const uint lettre = (uint)theword[indice]; merge_sorted_dequeue(new_runs, thenfa->trans->edges[etat][lettre]); } delete_dequeue(runs); runs = new_runs; size = size_dequeue(runs); indice++; } return runs; }