/** * Looks for a loop. To do that, we only follow transitions that can match E. * Every time we follow such a transition, we add it to the 'transitions' list. * This list is used to print the E loop if we find any. The function returns * 1 if a loop is found; 0 otherwise. */ static int find_an_E_loop(int* mark,int current_state,int graph,GrfCheckInfo* chk, struct list_pointer* transitions) { if (mark[current_state]==1) { /* The state has been visited, nothing to do */ return 0; } if (mark[current_state]==2) { /* The state is being visited, we have a loop */ error("E loop in graph %S, made of the following tags:\n",chk->fst2->graph_names[graph]); print_reversed_list(transitions,chk->fst2,current_state,0); return 1; } /* We start visiting the state */ mark[current_state]=2; SingleGraphState s=chk->condition_graphs[graph]->states[current_state]; Transition* t=s->outgoing_transitions; while (t!=NULL) { if (transition_can_match_E(t->tag_number,chk)) { struct list_pointer* new_head=new_list_pointer(t,transitions); int res=find_an_E_loop(mark,t->state_number,graph,chk,new_head); new_head->next=NULL; free_list_pointer(new_head); if (res==1) { return 1; } } t=t->next; } /* The state has been fully visited */ mark[current_state]=1; return 0; }
/** * Prints the graph call sequence that leads to the graph #n. */ void print_reversed_list(const struct list_int* l,int n,unichar** graph_names,U_FILE* ferr) { if (l->n==n) { error("ERROR: %S",graph_names[l->n]); if (ferr != NULL) u_fprintf(ferr,"ERROR: %S",graph_names[l->n]); return; } print_reversed_list(l->next,n,graph_names,ferr); error(" calls %S that",graph_names[l->n]); if (ferr != NULL) u_fprintf(ferr," calls %S that",graph_names[l->n]); }
/** * Returns 1 and prints an error message if a recursion is found in graph #n; * returns 0 otherwise. */ int look_for_recursion(int n,struct list_int* l,Fst2* fst2,int* graphs_matching_E,U_FILE*ferr) { if (is_in_list(n,l)) { /* If we find a graph that has already been visited */ print_reversed_list(l,n,fst2->graph_names,ferr); error(" recalls the graph %S\n",fst2->graph_names[n]); if (ferr != NULL) u_fprintf(ferr," recalls the graph %S\n",fst2->graph_names[n]); return 1; } l=new_list_int(n,l); int ret=explore_state(fst2->initial_states[n],l,fst2,graphs_matching_E,ferr); delete_head(&l); return ret; }
static int find_a_left_recursion(int* mark_graph,int* mark_state,int current_state,int graph, GrfCheckInfo* chk,struct list_pointer* transitions) { if (mark_state[current_state]==1) { /* The state has been visited, nothing to do */ return 0; } if (mark_state[current_state]==2) { /* The state is being visited, we have a loop, but it should have been detected before */ error("E loop in graph %S, made of the following tags:\n",chk->fst2->graph_names[graph]); print_reversed_list(transitions,chk->fst2,current_state,0); return 1; } /* We start visiting the state */ mark_state[current_state]=2; SingleGraphState s=chk->condition_graphs[graph]->states[current_state]; Transition* t=s->outgoing_transitions; while (t!=NULL) { if (t->tag_number<0) { /* As we look for left recursions, we always test recursively * graph calls, regardless the fact that they may match E */ struct list_pointer* new_head=new_list_pointer(t,transitions); int res=is_left_recursion(chk,-(t->tag_number),mark_graph,new_head); new_head->next=NULL; free_list_pointer(new_head); if (res==1) { return 1; } } if (transition_can_match_E(t->tag_number,chk)) { struct list_pointer* new_head=new_list_pointer(t,transitions); int res=find_a_left_recursion(mark_graph,mark_state,t->state_number,graph,chk,new_head); new_head->next=NULL; free_list_pointer(new_head); if (res==1) { return 1; } } t=t->next; } /* The state has been fully visited */ mark_state[current_state]=1; return 0; }
/** * By convention, if stop is >=0 it represents a state number, and if <0, it represents * a graph call. We use it as a stop condition. */ static void print_reversed_list(struct list_pointer* list,Fst2* fst2,int stop,int depth) { if (list==NULL) { return; } Transition* t=(Transition*)list->pointer; if (depth!=0) { if ((stop>=0 && t->state_number==stop) || (stop<0 && t->tag_number==stop)) { return; } } print_reversed_list(list->next,fst2,stop,depth+1); if (t->tag_number<0) { error(" :%S",fst2->graph_names[-t->tag_number]); } else { error(" %S",fst2->tags[t->tag_number]->input); if (fst2->tags[t->tag_number]->output!=NULL && fst2->tags[t->tag_number]->output[0]!='\0') { error("/%S",fst2->tags[t->tag_number]->output); } } error("\n"); }
/** * Returns 0 if no left recursion is found. Otherwise, prints a message that * describes the loop and returns 1. */ static int is_left_recursion(GrfCheckInfo* chk,int graph,int* mark_graph,struct list_pointer* transitions) { if (mark_graph[graph]==1) { /* The graph has already been tested for left recursions */ return 0; } if (mark_graph[graph]==2) { /* We found a left recursion */ error("Left recursion found in graph %S, made of the following tags:\n",chk->fst2->graph_names[graph]); print_reversed_list(transitions,chk->fst2,-graph,0); return 1; } mark_graph[graph]=2; SingleGraph g=chk->condition_graphs[graph]; /* 0 means that the state has not been visited at all * 1 means that the state has already been fully visited * 2 means that the state is being visited now, so finding such * a state means that there is an E loop */ int* mark_state=(int*)calloc(g->number_of_states,sizeof(int)); int recursion=0; int initial_state=get_initial_state(g); if (initial_state==-2) { fatal_error("Internal error: several initial states in graph %S\n",chk->fst2->graph_names[graph]); } if (initial_state==-1) { /* If the graph could not be loaded, we just ignore */ mark_graph[graph]=1; free(mark_state); return 0; } if (find_a_left_recursion(mark_graph,mark_state,initial_state,graph,chk,transitions)) { recursion=1; } free(mark_state); mark_graph[graph]=1; return recursion; }