/*****************************/ /* Gestionnaire de commandes */ /*****************************/ #include "shell_commands.h" /******************************************************************************/ /* Récupération des automates désignés par une commande */ /******************************************************************************/ genob *shell_genob_from_command(com_command *thecom) { // Si la commande est mal formée if (thecom == NULL || thecom->main == NULL) { return NULL; } // On récupère l'objet correspondant au premier maillon int i = object_get_from_name(thecom->main->string); if (i == -1) { return NULL; } // L'objet généralisé qu'on va retourner genob *new = NULL; // La chaîne complète string_chain *thechain = thecom->main; // Tant qu'il y a des maillons while (thechain != NULL) { // On prend le prochain maillon thechain = thechain->next; // On regarde le type de l'objet switch (objects[i]->type) { case LANGUAGE: { // Si il n'y pas de maillon suivant et pas de paramètre // La commande désigne simplement le langage if (thechain == NULL && thecom->params == NULL) { MALLOC(new, 1); new->type = OG_LANG; new->theob = objects[i]->lan; return new; } // Sinon la commande continue pour désigner l'automate minimal. com_keyword key = key_from_string_chain(thechain); switch (key) { case CM_MINI: i = shell_compute_minimal(i); break; default: return NULL; break; } } break; case AUTOMATON: { // Pour l'instant seul le cas d'un automate simple est géré if (thechain == NULL && thecom->params == NULL) { MALLOC(new, 1); new->type = OG_AUTO; new->theob = objects[i]->aut->nfa; return new; } else { return NULL; } } default: break; } } return NULL; } /******************************/ /* Application d'une commande */ /******************************/ static bool com_apply_command_rec(string_chain *thechain, com_parameters *pars, int i) { if (i == -1) { shell_command_error(); return false; } switch (objects[i]->type) { case LANGUAGE: { language *thelang = objects[i]->lan; if (thechain == NULL && pars == NULL) { if (thelang->type == SPE_REG) { fprintf(stdout, "This language is specified by a regular expression:\n"); reg_print(thelang->reg); } else if (thelang->type == SPE_NFA) { fprintf(stdout, "This language is specified by an automaton:\n"); nfa_view(objects[thelang->nfa]->aut->nfa); } return true; } com_keyword key = key_from_string_chain(thechain); switch (key) { case CM_MINI: shell_compute_minimal(i); return com_apply_command_rec(thechain->next, pars, thelang->minauto); break; default: shell_command_error(); return false; break; } } break; case AUTOMATON: { automata *theauto = objects[i]->aut; if (thechain == NULL && pars == NULL) { print_title_box(100, true, stdout, 1, "Automaton."); nfa_view(theauto->nfa); return true; } com_keyword key = key_from_string_chain_single(thechain); switch (key) { case CM_RUN: shell_make_nfa_run(theauto, pars); break; default: shell_command_error(); return false; break; } } break; default: shell_command_error(); return false; break; } return false; } bool com_apply_command(com_command *thecom) { if (thecom == NULL || thecom->main == NULL) { shell_command_error(); return false; } // On commence par regarder si le premier maillon de la commande correspond // à // une variable. int i = object_get_from_name(thecom->main->string); if (i != -1) { // Si c'est le cas, la commande est récursive return com_apply_command_rec(thecom->main->next, thecom->params, i); } // Sinon, le premier maillon doit être le seul et doit correspondre à un // keyword if (thecom->main->next) { shell_command_error(); return false; } com_keyword key = string_to_keyword(thecom->main->string); switch (key) { case CM_SAVE: return shell_save_to_file(thecom->params); break; case CM_CODE: return shell_code_to_file(thecom->params); break; case CM_SAVESESSION: return shell_save_session(thecom->params); break; case CM_LOADSESSION: return shell_load_session(thecom->params); break; case CM_DELETE: return shell_delete(thecom->params); break; case CM_RESET: return shell_reset(thecom->params); break; case CM_SORT: return shell_sort(thecom->params); break; case CM_BMCK: return shell_mccluskey_reg(NULL, thecom->params); break; case CM_THOMPSON: return shell_thompson_nfa(NULL, thecom->params); break; case CM_GLUSHKOV: return shell_glushkov_nfa(NULL, thecom->params); break; case CM_HOPCROFT: return shell_hopcroft_nfa(NULL, thecom->params); break; case CM_MIRROR: return shell_mirror_nfa(NULL, thecom->params); break; case CM_TRIMNFA: return shell_trim_nfa(NULL, thecom->params); break; case CM_KLEENE: return shell_kleene_nfa(NULL, thecom->params); break; case CM_INTERSEC: return shell_intersect_nfa(NULL, thecom->params); break; case CM_CONCAT: return shell_concat_nfa(NULL, thecom->params); break; case CM_UNION: return shell_union_nfa(NULL, thecom->params); break; case CM_DETERMIN: return shell_determinize_nfa(NULL, thecom->params); break; case CM_MINI: case CM_BRZOZO: return shell_minimize_nfa(NULL, thecom->params); break; case CM_OPEN: return shell_open_object(NULL, thecom->params); break; default: shell_command_error(); return false; break; } shell_command_error(); return false; } int com_apply_link_command(char *name, com_command *thecom) { if (thecom == NULL || thecom->main == NULL) { shell_command_error(); return -1; } if (!check_varname(name)) { return -1; } // Premier cas: la commande est juste un texte à traiter comme une regexp if (thecom->thetype == CMT_RAW) { regexp *myexp; myexp = parse_string_regexp(thecom->main->string); if (myexp == NULL) { return -1; } int i = object_add_language_reg(name, myexp); reg_print(myexp); return i; } // Si le premier maillon est un nom de variable, alors il s'agit juste de // faire une copie int i = index_from_string_chain(thecom->main); if (i != -1 && thecom->params == NULL) { object_copy_generic(i, name); return true; } // On sait maintenant que le premier argument est un keyword // Dans ce cas il ne peut pas y avoir de nesting. com_keyword key = key_from_string_chain_single(thecom->main); switch (key) { case CM_BMCK: return shell_mccluskey_reg(name, thecom->params); break; case CM_THOMPSON: return shell_thompson_nfa(name, thecom->params); break; case CM_GLUSHKOV: return shell_glushkov_nfa(name, thecom->params); break; case CM_HOPCROFT: return shell_hopcroft_nfa(name, thecom->params); break; case CM_MIRROR: return shell_mirror_nfa(name, thecom->params); break; case CM_TRIMNFA: return shell_trim_nfa(name, thecom->params); break; case CM_KLEENE: return shell_kleene_nfa(name, thecom->params); break; case CM_INTERSEC: return shell_intersect_nfa(name, thecom->params); break; case CM_CONCAT: return shell_concat_nfa(name, thecom->params); break; case CM_UNION: return shell_union_nfa(name, thecom->params); break; case CM_DETERMIN: return shell_determinize_nfa(name, thecom->params); break; case CM_MINI: case CM_BRZOZO: return shell_minimize_nfa(name, thecom->params); break; case CM_LINK: return shell_linktolang_nfa(name, thecom->params); break; case CM_OPEN: return shell_open_object(name, thecom->params); break; default: shell_command_error(); return false; break; } shell_command_error(); return false; } /******************************/ /* Calculs de nouveaux objets */ /******************************/ // Ajout d'un nouvel objet en copiant un objet déjà existant dans la table int object_copy_generic(int i, char *newname) { if (i == -1) { fprintf(stderr, "Error: this is not a valid object to copy.\n"); return -1; } switch (objects[i]->type) { case AUTOMATON: return object_add_automata(newname, nfa_copy(objects[i]->aut->nfa)); break; case LANGUAGE: if (objects[i]->lan->type == SPE_REG) { return object_add_language_reg(newname, reg_copy(objects[i]->lan->reg)); } else { int j = object_add_automata( newname, nfa_copy(objects[objects[i]->lan->nfa]->aut->nfa)); return object_add_language_nfa(newname, j); } default: return -1; break; } } // Calcule un nouveau NFA avec la méthode de Glushkov à partir d'un langage. int shell_glushkov_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 1) { shell_arguments_error(); return -1; } int i = index_from_string_chain(pars->param->main); if (i == -1) { shell_variable_error(); return false; } if (i < 0 || objects[i]->type != LANGUAGE || objects[i]->lan->type != SPE_REG) { fprintf(stderr, "Error: Glushkov's algorithm can only be applied to a " "language specified by a regular expression.\n"); return -1; } DEBUG("Computing an automaton from a language"); nfa *automaton = reg_glushkov(objects[i]->lan->reg); if (automaton == NULL) { fprintf(stderr, "Error: unable to compute the Glushkov automaton.\n" "Please implement reg_glushkov().\n"); return -1; } if (varname) { return object_add_automata(varname, automaton); } else { nfa_view(automaton); nfa_delete(automaton); return 1; } } // Calcule le miroir d'un nfa à partir d'un langage. int shell_mirror_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 1) { shell_arguments_error(); return -1; } int i = index_from_string_chain(pars->param->main); if (i == -1) { shell_variable_error(); return false; } if (i < 0 || objects[i]->type != AUTOMATON) { fprintf(stderr, "Error: Can only make the mirror of a NFA.\n"); return -1; } DEBUG("Computing the mirror of an automaton"); nfa *automaton = nfa_mirror(objects[i]->aut->nfa); if (automaton == NULL) { fprintf(stderr, "Error: unable to compute the mirror automaton.\n" "Please implement nfa_mirror().\n"); return -1; } if (varname) { return object_add_automata(varname, automaton); } else { nfa_view(automaton); nfa_delete(automaton); return 1; } } int shell_hopcroft_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 1) { shell_arguments_error(); return -1; } int i = index_from_string_chain(pars->param->main); if (i == -1) { shell_variable_error(); return false; } if (i < 0 || objects[i]->type != AUTOMATON) { fprintf(stderr, "Error: Can only minimize a NFA.\n"); return -1; } DEBUG("Minimizing with Hopcroft"); nfa *automaton = nfa_hopcroft(objects[i]->aut->nfa); if (automaton == NULL) { fprintf(stderr, "Error: unable to compute the Hopcroft automaton.\n" "Please implement nfa_hopcroft().\n"); return -1; } if (varname) { return object_add_automata(varname, automaton); } else { nfa_view(automaton); nfa_delete(automaton); return 1; } } int shell_mccluskey_reg(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 1) { shell_arguments_error(); return -1; } int i = index_from_string_chain(pars->param->main); if (i == -1) { shell_variable_error(); return false; } if (i < 0 || objects[i]->type != AUTOMATON) { fprintf(stderr, "Error: Brzozowski and McCluskey's algorithm can only be " "applied to a NFA.\n"); return -1; } DEBUG("Computing a regular expression from an automaton"); regexp *exp = nfa_mccluskey(objects[i]->aut->nfa); if (exp == NULL) { fprintf(stderr, "Error: unable to compute the regular expression.\n" "Please implement nfa_mccluskey().\n"); return -1; } if (varname) { return object_add_language_reg(varname, exp); } else { reg_print(exp); reg_free(exp); return 1; } } // Calcule un nouveau NFA avec la méthode de thompson à partir d'un langage. int shell_thompson_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 1) { shell_arguments_error(); return -1; } int i = index_from_string_chain(pars->param->main); if (i == -1) { shell_variable_error(); return false; } if (i < 0 || objects[i]->type != LANGUAGE || objects[i]->lan->type != SPE_REG) { fprintf(stderr, "Error: Thomson's algorithm can only be applied to a " "language specified by a regular expression.\n"); return -1; } DEBUG("Computing an automaton from a language"); nfa *automaton = reg_thompson(objects[i]->lan->reg); if (automaton == NULL) { fprintf(stderr, "Error: unable to compute the Thompson automaton.\n" "Please implement reg_thompson().\n"); return -1; } if (varname) { return object_add_automata(varname, automaton); } else { nfa_view(automaton); nfa_delete(automaton); return 1; } } // Union de deux NFAs int shell_union_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 2) { shell_arguments_error(); return -1; } int i1 = index_from_string_chain(pars->param->main); int i2 = index_from_string_chain(pars->next->param->main); if (i1 == -1 || i2 == -1) { shell_variable_error(); return false; } if (i1 < 0 || i2 < 0 || objects[i1]->type != AUTOMATON || objects[i2]->type != AUTOMATON) { fprintf(stderr, "Error: The union algorithm requires two automata as input.\n"); return -1; } nfa *automaton = nfa_union(objects[i1]->aut->nfa, objects[i2]->aut->nfa); if (automaton == NULL) { fprintf(stderr, "Error: unable to compute the union.\n" "Please implement nfa_union().\n"); return -1; } if (varname) { return object_add_automata(varname, automaton); } else { nfa_view(automaton); nfa_delete(automaton); return 1; } } // Concatène deux NFAs int shell_concat_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 2) { shell_arguments_error(); return -1; } int i1 = index_from_string_chain(pars->param->main); int i2 = index_from_string_chain(pars->next->param->main); if (i1 == -1 || i2 == -1) { shell_variable_error(); return false; } if (i1 < 0 || i2 < 0 || objects[i1]->type != AUTOMATON || objects[i2]->type != AUTOMATON) { fprintf( stderr, "Error: The concatenation algorithm requires two automata as input.\n"); return -1; } nfa *automaton = nfa_concat(objects[i1]->aut->nfa, objects[i2]->aut->nfa); if (automaton == NULL) { fprintf(stderr, "Error: unable to compute the concatenation.\n" "Please implement nfa_concat().\n"); return -1; } if (varname) { return object_add_automata(varname, automaton); } else { nfa_view(automaton); nfa_delete(automaton); return 1; } } // Étoile de Kleene sur un NFA int shell_kleene_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 1) { shell_arguments_error(); return -1; } int i = index_from_string_chain(pars->param->main); if (i == -1) { shell_variable_error(); return false; } if (i < 0 || objects[i]->type != AUTOMATON) { fprintf(stderr, "Error: The Kleene star algorithm can only be applied to " "an automaton.\n"); return -1; } nfa *automaton = nfa_star(objects[i]->aut->nfa); if (automaton == NULL) { fprintf(stderr, "Error: unable to compute the Kleene star automaton.\n" "Please implement nfa_star().\n"); return -1; } if (varname) { return object_add_automata(varname, automaton); } else { nfa_view(automaton); nfa_delete(automaton); return 1; } } // Calcule un nouveau NFA en supprimant tous les états non-accessibles ou non // co-accessibles int shell_trim_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 1) { shell_arguments_error(); return -1; } int i = index_from_string_chain(pars->param->main); if (i == -1) { shell_variable_error(); return false; } if (i < 0 || objects[i]->type != AUTOMATON) { fprintf(stderr, "Error: Can only trim an automaton.\n"); return -1; } nfa *automaton = nfa_trim(objects[i]->aut->nfa); if (automaton == NULL) { fprintf(stderr, "Error: unable to compute the trimmed automaton.\n" "Please implement nfa_trim().\n"); return -1; } if (varname) { return object_add_automata(varname, automaton); } else { nfa_view(automaton); nfa_delete(automaton); return 1; } } // Calcule un nouveau NFA en réalisant l'intersection de deux NFAs existants int shell_intersect_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 2) { shell_arguments_error(); return -1; } int i1 = index_from_string_chain(pars->param->main); int i2 = index_from_string_chain(pars->next->param->main); if (i1 == -1 || i2 == -1) { shell_variable_error(); return false; } if (i1 < 0 || i2 < 0 || objects[i1]->type != AUTOMATON || objects[i2]->type != AUTOMATON) { fprintf( stderr, "Error: The intersection algorithm requires two automata as input.\n"); return -1; } nfa *automaton = nfa_intersect(objects[i1]->aut->nfa, objects[i2]->aut->nfa, true); if (automaton == NULL) { fprintf(stderr, "Error: unable to compute the intersection automaton.\n" "Please implement nfa_intersect().\n"); return -1; } if (varname) { return object_add_automata(varname, automaton); } else { nfa_view(automaton); nfa_delete(automaton); return 1; } } // Calcule un nouveau NFA déterministe complet en déterminisant un NFA déjà // existant int shell_determinize_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 1) { shell_arguments_error(); return -1; } int i = index_from_string_chain(pars->param->main); if (i == -1) { shell_variable_error(); return false; } if (i < 0 || objects[i]->type != AUTOMATON) { fprintf(stderr, "Error: The subset construction can only be applied to an " "automaton.\n"); return -1; } nfa *automaton = nfa_determinize(objects[i]->aut->nfa, true); if (automaton == NULL) { fprintf(stderr, "Error: unable to compute the determinized automaton.\n" "Please implement nfa_determinize().\n"); return -1; } if (varname) { return object_add_automata(varname, automaton); } else { nfa_view(automaton); nfa_delete(automaton); return 1; } } // Calcule un automate minimal à partir d'un NFA int shell_minimize_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 1) { shell_arguments_error(); return -1; } int i = index_from_string_chain(pars->param->main); if (i == -1) { shell_variable_error(); return false; } if (i < 0 || objects[i]->type != AUTOMATON) { fprintf(stderr, "Error: the minimization algorithm can only be applied to " "an automaton.\n"); return -1; } nfa *automaton = nfa_brzozowski(objects[i]->aut->nfa); if (automaton == NULL) { fprintf(stderr, "Error: unable to compute the minimal automaton by " "Brzozowski's algorithm.\n" "Please implement nfa_brzozowski().\n"); return -1; } if (varname) { return object_add_automata(varname, automaton); } else { nfa_view(automaton); nfa_delete(automaton); return 1; } } // Links an existing nfa to a nex language int shell_linktolang_nfa(char *varname, com_parameters *pars) { if (com_nbparams(pars) != 1) { shell_arguments_error(); return -1; } int i = index_from_string_chain(pars->param->main); if (i == -1) { shell_variable_error(); return false; } if (i < 0 || objects[i]->type != AUTOMATON) { fprintf(stderr, "Error: the argument should be an automata variable.\n"); return -1; } return object_add_language_nfa(varname, i); } // Sauvegarde d'un objet bool shell_save_to_file(com_parameters *params) { if (com_nbparams(params) != 2) { shell_arguments_error(); return false; } com_command **pararray = com_getparamarray(params); int i = index_from_string_chain(pararray[0]->main); if (i == -1) { shell_variable_error(); return false; } if (!com_israw(pararray[1])) { fprintf(stderr, "Error: cannot parse the filename\n"); return false; } char *filename = pararray[1]->main->string; printf("saving %s in the file: \"%s\".\n", params->param->main->string, filename); files_save_object(objects[i], filename); free(pararray); return true; } // Sauvegarde sous forme de code d'un automate bool shell_code_to_file(com_parameters *params) { if (com_nbparams(params) != 2) { shell_arguments_error(); return false; } com_command **pararray = com_getparamarray(params); int i = index_from_string_chain(pararray[0]->main); if (i == -1) { shell_variable_error(); return false; } if (objects[i]->type != AUTOMATON) { fprintf(stderr, "Error: code can only be applied to an automaton\n"); return false; } if (!com_israw(pararray[1])) { fprintf(stderr, "Error: cannot parse the filename\n"); return false; } char *filename = pararray[1]->main->string; printf("sending the code of %s in file \"%s\".\n", params->param->main->string, filename); nfa2code(objects[i]->aut->nfa, filename); free(pararray); return true; } // Sauvegarde d'une session complète bool shell_save_session(com_parameters *params) { if (com_nbparams(params) != 1) { shell_arguments_error(); return false; } if (!com_israw(params->param)) { fprintf(stderr, "Error: cannot parse the filename\n"); return false; } char *filename = params->param->main->string; printf("saving the session in the file: \"%s\".\n", filename); files_save_session(filename); return false; } // Chargement d'une session à partir d'un fichier bool shell_load_session(com_parameters *params) { if (com_nbparams(params) != 1) { shell_arguments_error(); return false; } if (!com_israw(params->param)) { fprintf(stderr, "Error: cannot parse the filename\n"); return false; } char *filename = params->param->main->string; printf("loading the session saved in the file: \"%s\".\n", filename); files_load_session(filename); return false; } // Suppression d'un objet int shell_delete(com_parameters *params) { if (com_nbparams(params) != 1 || !com_single(params->param)) { shell_arguments_error(); return false; } return object_delete_from_name(params->param->main->string); } // Effacement des noms d'un automate int shell_reset(com_parameters *params) { if (com_nbparams(params) != 1 || !com_single(params->param)) { shell_arguments_error(); return false; } int i = object_get_from_name(params->param->main->string); if (i == -1) { fprintf(stderr, "Error: unknown variable.\n"); return -1; } if (objects[i]->type != AUTOMATON || objects[i]->aut == NULL) { shell_arguments_error(); return false; } nfa_reset_state_names(objects[i]->aut->nfa); return true; } // Tri des objets bool shell_sort(com_parameters *params) { if (params != NULL) { shell_arguments_error(); return false; } object_sort_array(); return true; } // Ouverture d'un objet bool shell_open_object(char *varname, com_parameters *params) { if (com_nbparams(params) != 1 || params->param->thetype != CMT_RAW || varname == NULL) { shell_arguments_error(); return false; } files_read_object(params->param->main->string, varname); return true; } // Calcul d'un NFA aléatoire bool shell_random_nfa(char *, com_parameters *, bool) { /* if (com_nbparams(params) != 3) { shell_arguments_error(); return false; } com_command* arg1 = params->param; com_command* arg2 = params->next->param; com_command* arg3 = params->next->next->param; if (!com_single(arg1) || !com_single(arg2) || !com_single(arg3)) { shell_arguments_error(); return false; } char* end; int nb1 = strtol(arg1->main->string, &end, 10); if (*end != '\0') { shell_arguments_error(); return false; } int nb2 = strtol(arg2->main->string, &end, 10); if (*end != '\0') { shell_arguments_error(); return false; } int nb3 = strtol(arg3->main->string, &end, 10); if (*end != '\0') { shell_arguments_error(); return false; } */ return false; /* if (det) return object_add_automata(varname, dfa_random(nb1, nb2, nb3)); else return object_add_automata(varname, nfa_random(nb1, nb2, nb3)); */ } /********************************************************************/ /* Affichage - fonctions appellées par le gestionnaire de commandes */ /********************************************************************/ bool shell_make_nfa_run(automata *aut, com_parameters *pars) { if (com_nbparams(pars) != 1 || !com_single(pars->param)) { shell_arguments_error(); return false; } dequeue *states = nfa_compute_runs(aut->nfa, pars->param->main->string); if (states == NULL) { printf("Error: this is not a valid word for this automaton.\n"); return false; } printf("Set of states reached: "); if (isempty_dequeue(states)) { printf("∅.\n"); } else { printf("{"); for (uint i = 0; i < size_dequeue(states) - 1; i++) { nfa_print_state(aut->nfa, lefread_dequeue(states, i), stdout); printf(","); } nfa_print_state(aut->nfa, lefread_dequeue(states, size_dequeue(states) - 1), stdout); printf("}.\n"); } // On teste si on a atteint un état final uint i = 0; uint j = 0; while (i < size_dequeue(states) && j < size_dequeue(aut->nfa->finals)) { if (lefread_dequeue(states, i) == lefread_dequeue(aut->nfa->finals, j)) { printf("The word is accepted.\n"); return true; } else if (lefread_dequeue(states, i) < lefread_dequeue(aut->nfa->finals, j)) { i++; } else { j++; } } printf("The word is rejected.\n"); return true; }