/** * This function optimizes a pattern of the form "eat". */ void optimize_token_pattern(int i,Fst2Tag* tag,Alphabet* alph, struct locate_parameters* p,Abstract_allocator prv_alloc) { /* Whatever happens, this pattern will be turned into a token list */ tag[i]->type=TOKEN_LIST_TAG; unichar* opt_token=tag[i]->pattern->inflected; /* First, we check if this token pattern can recognize some tag tokens */ struct list_int* list=p->tag_token_list; while (list!=NULL) { struct dela_entry* entry=tokenize_tag_token(p->tokens->value[list->n],1); if ((!is_bit_mask_set(tag[i]->control,RESPECT_CASE_TAG_BIT_MASK) && is_equal_or_uppercase(opt_token,entry->inflected,alph)) || !u_strcmp(opt_token,entry->inflected)) { tag[i]->matching_tokens=sorted_insert(list->n,tag[i]->matching_tokens,prv_alloc); } free_dela_entry(entry); list=list->next; } /* Then, we look for normal tokens */ if (is_bit_mask_set(tag[i]->control,RESPECT_CASE_TAG_BIT_MASK)) { /* If no case variants are allowed, then we just have to insert the number * of the token, but only if this token in the text ones. */ int token_number; if (-1!=(token_number=get_value_index(opt_token,p->tokens,DONT_INSERT))) { tag[i]->matching_tokens=sorted_insert(token_number,tag[i]->matching_tokens,prv_alloc); } return; } /* Here, we have to get all the case variants of the token. */ tag[i]->matching_tokens=destructive_sorted_merge(get_token_list_for_sequence(opt_token,alph,p->tokens,prv_alloc),tag[i]->matching_tokens,prv_alloc); }
/** * This function takes a condition, i.e. a list of graph numbers. Then, it * tests if all the corresponding graphs match <E>. In that case, it sets * '*matches_E' to E_IS_MATCHED. */ struct list_int* resolve_simple_condition(struct list_int* c,Fst2State* states, int* initial_states,int *modification, int *matches_E) { struct list_int* tmp; *matches_E=E_IS_MATCHED; if (c==NULL) return NULL; /* First, we try to solve the rest of the condition */ tmp=resolve_simple_condition(c->next,states,initial_states,modification,matches_E); if ((*matches_E)==E_IS_NOT_MATCHED) { /* If at least one element of the rest of the condition does not * matches <E>, we can delete the current element (the rest has * already been freed). */ free(c); (*modification)++; return NULL; } if ((*matches_E)==E_IS_MATCHED) { /* If all the elements of the rest of the condition match <E> */ c->next=tmp; if (is_bit_mask_set(states[initial_states[c->n]]->control,UNCONDITIONAL_E_MATCH)) { /* If the current one also matches <E>, then we can return */ return c; } if (is_bit_mask_set(states[initial_states[c->n]]->control,CONDITIONAL_E_MATCH) && !is_bit_mask_set(states[initial_states[c->n]]->control,DOES_NOT_MATCH_E)) { /* If we don't know if the current element matches <E> or not */ *matches_E=DOES_NOT_KNOW_IF_E_IS_MATCHED; return c; } /* If the current element does not match <E>, we can free the rest of the condition */ free_list_int(c); *matches_E=E_IS_NOT_MATCHED; (*modification)++; return NULL; } /* If we don't know if the rest of the condition match <E> */ c->next=tmp; if (is_bit_mask_set(states[initial_states[c->n]]->control,UNCONDITIONAL_E_MATCH)) { /* If the current element matches <E>, we still cannot decide */ *matches_E=DOES_NOT_KNOW_IF_E_IS_MATCHED; return c; } if (is_bit_mask_set(states[initial_states[c->n]]->control,DOES_NOT_MATCH_E)) { /* If the current element does not matches <E>, the condition is not verified */ free_list_int(c); *matches_E=E_IS_NOT_MATCHED; (*modification)++; return NULL; } /* If we still don't know, we do nothing */ *matches_E=DOES_NOT_KNOW_IF_E_IS_MATCHED; return c; }
/** * Explores the transitions that outgo from the given state. * Returns 1 if a recursion is found; 0 otherwise. */ int explore_state(int state_number,struct list_int* l,Fst2* fst2,int* graphs_matching_E,U_FILE*ferr) { Fst2State s=fst2->states[state_number]; int ret=0; if (s==NULL) return 0; if (is_bit_mask_set(s->control,TMP_LOOP_MARK|VISITED_MARK)) { /* If this state has already been processed */ return 0; } set_bit_mask(&(s->control),TMP_LOOP_MARK|VISITED_MARK); Transition* list=s->transitions; while (list!=NULL) { if (list->tag_number<0) { /* If we have a subgraph call */ if (look_for_recursion(-(list->tag_number),l,fst2,graphs_matching_E,ferr)) { /* If there is a recursion */ return 1; } if (graphs_matching_E[-list->tag_number] && explore_state(list->state_number,l,fst2,graphs_matching_E,ferr)) { /* If the graph matches <E> */ return 1; } } else if (fst2->tags[list->tag_number]->control==1) { /* If we have a transition that can match <E> */ if (explore_state(list->state_number,l,fst2,graphs_matching_E,ferr)) { return 1; } } list=list->next; } unset_bit_mask(&(s->control),TMP_LOOP_MARK); return ret; }
/** * Returns 1 if the given state has already been visited; 0 otherwise. */ int look_for_E_loop_in_state(int state_number,Fst2* fst2,int* graphs_matching_E) { Transition* l; Fst2State e=fst2->states[state_number]; if (is_bit_mask_set(e->control,TMP_LOOP_MARK)) { /* If we have already visited this state */ unset_bit_mask(&(e->control),TMP_LOOP_MARK); return 1; } set_bit_mask(&(e->control),TMP_LOOP_MARK); l=e->transitions; while (l!=NULL) { if (l->tag_number<0) { /* If we have a graph call */ if (graphs_matching_E[-l->tag_number]) { /* And if we can cross it because it matches <E> */ if (look_for_E_loop_in_state(l->state_number,fst2,graphs_matching_E)) { /* And if we have reached the current state via an <E> loop */ unset_bit_mask(&(e->control),TMP_LOOP_MARK); return 1; } } } else if (fst2->tags[l->tag_number]->control==1) { /* If we have a tag that can match <E> */ if (look_for_E_loop_in_state(l->state_number,fst2,graphs_matching_E)) { /* And if we have reached the current state via an <E> loop */ unset_bit_mask(&(e->control),TMP_LOOP_MARK); return 1; } } l=l->next; } unset_bit_mask(&(e->control),TMP_LOOP_MARK); return 0; }
/** * This function looks for each graph if it can resolve a condition. * * It returns a non zero value if some conditions have been resolved, * even just one; 0 otherwise. */ int resolve_conditions(ConditionList* conditions,int n_graphs, Fst2State* states,int* initial_states,U_FILE*ferr) { int modification=0; for (int i=1;i<n_graphs+1;i++) { if (!is_bit_mask_set(states[initial_states[i]]->control, UNCONDITIONAL_E_MATCH|DOES_NOT_MATCH_E)) { /* If we don't already know the status of the current graph */ if (!is_bit_mask_set(states[initial_states[i]]->control,CONDITIONAL_E_MATCH)) { /* If there is no conditionnal match, then we say that it does * not match <E> */ set_bit_mask(&(states[initial_states[i]]->control),DOES_NOT_MATCH_E); modification++; } else { /* Otherwise, we try to solve the conditions of the graph */ modification=modification+resolve_conditions_for_one_graph(i,conditions,states,initial_states,ferr); } } } return modification; }
/** * Cleans the control bytes of the fst2's states. If 'graphs_matching_E' * is not null, graphs_matching_E[i] is set to 1 if the graph #i matches <E>; * 0 otherwise. */ void clean_controls(Fst2* fst2,int* graphs_matching_E) { int i; if (graphs_matching_E!=NULL) { for (i=1;i<fst2->number_of_graphs+1;i++) { if (is_bit_mask_set(fst2->states[fst2->initial_states[i]]->control,UNCONDITIONAL_E_MATCH)) graphs_matching_E[i]=1; else graphs_matching_E[i]=0; } } int ALL_MASKS=CONDITIONAL_E_MATCH|UNCONDITIONAL_E_MATCH|DOES_NOT_MATCH_E|TMP_LOOP_MARK|VISITED_MARK; for (i=0;i<fst2->number_of_states;i++) { unset_bit_mask(&(fst2->states[i]->control),(unsigned char)ALL_MASKS); } }
/** * This function optimizes the given transition. */ static void optimize_transition(Variables* v,OutputVariables* output,Fst2* fst2,Transition* transition, OptimizedFst2State state,Fst2Tag* tags,Abstract_allocator prv_alloc) { if (transition->tag_number<0) { /* If the transition is a graph call */ add_graph_call(transition,&(state->graph_calls),prv_alloc); add_graph_call(transition,&(state->unoptimized_graph_calls),prv_alloc); return; } Fst2Tag tag=tags[transition->tag_number]; if (tag==NULL) { fatal_error("NULL transition tag in optimize_transition\n"); } int negation=is_bit_mask_set(tag->control,NEGATION_TAG_BIT_MASK); /* First, we look if there is a compound pattern associated to this tag */ if (tag->compound_pattern!=NO_COMPOUND_PATTERN) { add_pattern(tag->compound_pattern,transition,&(state->compound_patterns),negation,prv_alloc); } /* Then, we look the possible kind of transitions */ switch (tag->type) { case TOKEN_LIST_TAG: add_token_list(tag->matching_tokens,transition,&(state->token_list),&(state->number_of_tokens),prv_alloc); return; case PATTERN_NUMBER_TAG: add_pattern(tag->pattern_number,transition,&(state->patterns),negation,prv_alloc); return; case META_TAG: { add_meta(tag->meta,transition,&(state->metas),negation,prv_alloc); add_meta(tag->meta,transition,&(state->unoptimized_metas),negation,prv_alloc); return; } case BEGIN_VAR_TAG: { add_input_variable(v,tag->variable,transition,&(state->input_variable_starts),prv_alloc); add_input_variable(v,tag->variable,transition,&(state->unoptimized_input_variable_starts),prv_alloc); return; } case END_VAR_TAG: { add_input_variable(v,tag->variable,transition,&(state->input_variable_ends),prv_alloc); add_input_variable(v,tag->variable,transition,&(state->unoptimized_input_variable_ends),prv_alloc); return; } case BEGIN_OUTPUT_VAR_TAG: { add_output_variable(output,tag->variable,transition,&(state->output_variable_starts),prv_alloc); add_output_variable(output,tag->variable,transition,&(state->unoptimized_output_variable_starts),prv_alloc); return; } case END_OUTPUT_VAR_TAG: { add_output_variable(output,tag->variable,transition,&(state->output_variable_ends),prv_alloc); add_output_variable(output,tag->variable,transition,&(state->unoptimized_output_variable_ends),prv_alloc); return; } case BEGIN_POSITIVE_CONTEXT_TAG: add_positive_context(fst2,state,transition,prv_alloc); return; case BEGIN_NEGATIVE_CONTEXT_TAG: add_negative_context(fst2,state,transition,prv_alloc); return; case END_CONTEXT_TAG: add_end_context(state,transition,prv_alloc); return; case LEFT_CONTEXT_TAG: { add_meta(META_LEFT_CONTEXT,transition,&(state->metas),0,prv_alloc); add_meta(META_LEFT_CONTEXT,transition,&(state->unoptimized_metas),0,prv_alloc); return; } case BEGIN_MORPHO_TAG: { struct opt_meta* new_meta=add_meta(META_BEGIN_MORPHO,transition,&(state->metas),0,prv_alloc); get_reachable_closing_morphological_mode(fst2,transition->state_number,&(new_meta->morphological_mode_ends),prv_alloc); add_meta(META_BEGIN_MORPHO,transition,&(state->unoptimized_metas),0,prv_alloc); return; } case END_MORPHO_TAG: { add_meta(META_END_MORPHO,transition,&(state->metas),0,prv_alloc); add_meta(META_END_MORPHO,transition,&(state->unoptimized_metas),0,prv_alloc); return; } case TEXT_START_TAG: { add_meta(META_TEXT_START,transition,&(state->metas),0,prv_alloc); add_meta(META_TEXT_START,transition,&(state->unoptimized_metas),0,prv_alloc); return; } case TEXT_END_TAG: { add_meta(META_TEXT_END,transition,&(state->metas),0,prv_alloc); add_meta(META_TEXT_END,transition,&(state->unoptimized_metas),0,prv_alloc); return; } default: fatal_error("Unexpected transition tag type in optimize_transition\n"); } }
/** * This function analyses the inputs of all the tags of the given .fst2 in * order to determine their kind. 'tokens' contains all the text tokens. * After the execution of the function, 'number_of_patterns' will contain * the number of patterns found in the grammar, and 'is_DIC'/'is_CDIC'/'is_SDIC'/'is_TDIC' * will contain 1 if the tag 'DIC'/'CDIC'/'SDIC'/'TDIC' has been found. See * the comment below about the special case for '<!DIC>'. */ void process_tags(int *number_of_patterns, struct string_hash* semantic_codes, int *is_DIC,int *is_CDIC, int *is_SDIC,struct locate_parameters* parameters, Abstract_allocator prv_alloc) { (*number_of_patterns)=0; (*is_DIC)=0; (*is_CDIC)=0; (*is_SDIC)=0; Fst2* fst2=parameters->fst2; struct string_hash* tokens=parameters->tokens; Fst2Tag* tag=fst2->tags; /* We get the number of the SPACE token */ unichar t[2]; t[0]=' '; t[1]='\0'; parameters->SPACE=get_value_index(t,tokens,DONT_INSERT); /* Then, we test all the tags */ for (int i=0;i<fst2->number_of_tags;i++) { if (tag[i]->type!=UNDEFINED_TAG) { /* We don't need to process again things like variables and contexts * that have already been processed at the time of loading the fst2 */ continue; } int length=u_strlen(tag[i]->input); if (!u_strcmp(tag[i]->input,"#")) { /* If we have a #, we must check if it is the meta one that * forbids space or the "#" token */ if (is_bit_mask_set(tag[i]->control,RESPECT_CASE_TAG_BIT_MASK)) { /* If the case respect bit is set to 1, then we have the "#" token */ tag[i]->type=PATTERN_TAG; tag[i]->pattern=build_token_pattern(tag[i]->input,prv_alloc); } else { /* If we have the meta # */ tag[i]->type=META_TAG; tag[i]->meta=META_SHARP; } } else if (!u_strcmp(tag[i]->input,"<E>")) { /* If we have a transition tagged by the empty word epsilon */ tag[i]->type=META_TAG; tag[i]->meta=META_EPSILON; } else { int token_number=get_value_index(tag[i]->input,tokens,DONT_INSERT); if (token_number!=-1) { /* If the input is an existing token */ if (token_number==parameters->SPACE) { /* If it is a space */ tag[i]->type=META_TAG; tag[i]->meta=META_SPACE; } else { /* If it is a normal token */ tag[i]->type=PATTERN_TAG; tag[i]->pattern=build_token_pattern(tag[i]->input,prv_alloc); } } else { /* This input is not an existing token. Two cases can happen: * 1) metas like <!MOT> or patterns like <V:K> * 2) a word that is not in the text tokens */ if (tag[i]->input[0]!='<' || tag[i]->input[length-1]!='>') { /* If we are in case 2, it may not be an error. For instance, * if the tag contains "foo" and if it is a tag that allows * case variations, we could match "FOO" if this token is in the * text. */ tag[i]->type=PATTERN_TAG; tag[i]->pattern=build_token_pattern(tag[i]->input,prv_alloc); } else { /* If we have something of the form <...>, we must test first if it is * or not a negative tag like <!XXX> */ int negative_tag=(tag[i]->input[1]=='!')?1:0; if (negative_tag) { set_bit_mask(&(tag[i]->control),NEGATION_TAG_BIT_MASK); } /* Then, we must test if we have or not a meta. To do that, we * extract the content without < > and ! if any.*/ unichar* content=u_strdup(&(tag[i]->input[1+negative_tag]),length-2-negative_tag,prv_alloc); /* And we test all the possible metas */ if (!u_strcmp(content,"MOT")) { tag[i]->type=META_TAG; tag[i]->meta=META_MOT; } else if (!u_strcmp(content,"DIC")) { tag[i]->type=META_TAG; tag[i]->meta=META_DIC; if (!negative_tag) { /* We mark that the DIC tag has been found, but only * if it is not the negative one (<!DIC>). We do this * because things matched by <DIC> will be taken from * the 'dlf' and 'dlc' files, whereas things matched by <!DIC> * will be taken from the 'err' file. Such a trick is necessary * if we don't want 'priori' to be taken as an unknown word since * it is part of the compound word 'a priori' */ (*is_DIC)=1; } } else if (!u_strcmp(content,"CDIC")) { tag[i]->type=META_TAG; tag[i]->meta=META_CDIC; (*is_CDIC)=1; } else if (!u_strcmp(content,"SDIC")) { tag[i]->type=META_TAG; tag[i]->meta=META_SDIC; (*is_SDIC)=1; } else if (!u_strcmp(content,"TDIC")) { tag[i]->type=META_TAG; tag[i]->meta=META_TDIC; } else if (!u_strcmp(content,"MAJ")) { tag[i]->type=META_TAG; tag[i]->meta=META_MAJ; } else if (!u_strcmp(content,"MIN")) { tag[i]->type=META_TAG; tag[i]->meta=META_MIN; } else if (!u_strcmp(content,"PRE")) { tag[i]->type=META_TAG; tag[i]->meta=META_PRE; } else if (!u_strcmp(content,"NB")) { tag[i]->type=META_TAG; tag[i]->meta=META_NB; if (negative_tag) { error("Negative mark will be ignored in <!NB>\n"); } } else if (!u_strcmp(content,"TOKEN")) { tag[i]->type=META_TAG; tag[i]->meta=META_TOKEN; } else { /* If we arrive here, we have not a meta but a pattern like * <be>, <be.V>, <V:K>, ... */ tag[i]->type=PATTERN_TAG; tag[i]->pattern=build_pattern(content,semantic_codes,parameters->tilde_negation_operator,prv_alloc); if (tag[i]->pattern->type==CODE_PATTERN || tag[i]->pattern->type==LEMMA_AND_CODE_PATTERN || tag[i]->pattern->type==FULL_PATTERN || tag[i]->pattern->type==INFLECTED_AND_LEMMA_PATTERN) { /* If the pattern we obtain contains grammatical/semantic * codes, then we put it in the pattern tree and we note its number. */ tag[i]->pattern_number=add_pattern(number_of_patterns,tag[i]->pattern,parameters->pattern_tree_root,prv_alloc); if (tag[i]->pattern->type==CODE_PATTERN) { /* If we have a code pattern, then the tag will just need to contain * the pattern number, BUT, WE DO NOT FREE THE PATTERN, * since this pattern still could be used in morphological mode */ tag[i]->type=PATTERN_NUMBER_TAG; } } } /* We don't forget to free the content */ free_cb(content,prv_alloc); } } } } }
/** * Returns 1 if the given .fst2 is OK to be used by the Locate program; 0 otherwise. * Conditions are: * * 1) no left recursion * 2) no loop that can recognize the empty word (<E> with an output or subgraph * that can match the empty word). */ int OK_for_Locate_write_error(const char* name,char no_empty_graph_warning,U_FILE* ferr) { ConditionList* conditions; ConditionList* conditions_for_state; int i,j; int ERROR=0; struct FST2_free_info fst2_free; Fst2* fst2=load_abstract_fst2(name,1,&fst2_free); if (fst2==NULL) { fatal_error("Cannot load graph %s\n",name); } u_printf("Recursion detection started\n"); int* graphs_matching_E=(int*)malloc(sizeof(int)*(fst2->number_of_graphs+1)); conditions=(ConditionList*)malloc(sizeof(ConditionList)*(fst2->number_of_graphs+1)); if (graphs_matching_E==NULL || conditions==NULL) { fatal_alloc_error("OK_for_Locate"); } for (i=0;i<fst2->number_of_graphs+1;i++) { graphs_matching_E[i]=0; conditions[i]=NULL; } /* First, we look for tags that match the empty word <E> */ for (i=0;i<fst2->number_of_tags;i++) { check_epsilon_tag(fst2->tags[i]); } /* Then, we look for graphs that match <E> with or without conditions */ for (i=1;i<=fst2->number_of_graphs;i++) { conditions_for_state=(ConditionList*)malloc(sizeof(ConditionList)*fst2->number_of_states_per_graphs[i]); if (conditions_for_state==NULL) { fatal_alloc_error("OK_for_Locate"); } for (j=0;j<fst2->number_of_states_per_graphs[i];j++) { conditions_for_state[j]=NULL; } graphs_matching_E[i]=graph_matches_E(fst2->initial_states[i],fst2->initial_states[i], fst2->states,fst2->tags,i,fst2->graph_names, conditions_for_state,&conditions[i]); /* If any, we remove the temp conditions */ if (conditions[i]!=NULL) free_ConditionList(conditions[i]); /* And we way that the conditions for the current graph are its initial * state's ones. */ conditions[i]=conditions_for_state[0]; /* Then we perform cleaning */ conditions_for_state[0]=NULL; for (j=1;j<fst2->number_of_states_per_graphs[i];j++) { free_ConditionList(conditions_for_state[j]); } free(conditions_for_state); } /* Then, we use all our condition lists to determine which graphs match <E>. * We iterate until we find a fixed point. If some conditions remain non null * after this loop, it means that there are <E> dependencies between graphs * and this case will be dealt with later. */ u_printf("Resolving <E> conditions\n"); while (resolve_conditions(conditions,fst2->number_of_graphs, fst2->states,fst2->initial_states,ferr)) {} if (is_bit_mask_set(fst2->states[fst2->initial_states[1]]->control,UNCONDITIONAL_E_MATCH)) { /* If the main graph matches <E> */ if (!no_empty_graph_warning) { error("ERROR: the main graph %S recognizes <E>\n",fst2->graph_names[1]); if (ferr != NULL) u_fprintf(ferr,"ERROR: the main graph %S recognizes <E>\n",fst2->graph_names[1]); } ERROR=1; } if (!ERROR) { for (i=1;i<fst2->number_of_graphs+1;i++) { if (is_bit_mask_set(fst2->states[fst2->initial_states[i]]->control,UNCONDITIONAL_E_MATCH)) { /* If the graph matches <E> */ if (!no_empty_graph_warning) { error("WARNING: the graph %S recognizes <E>\n",fst2->graph_names[i]); if (ferr != NULL) u_fprintf(ferr,"WARNING: the graph %S recognizes <E>\n",fst2->graph_names[i]); } } } } clean_controls(fst2,graphs_matching_E); if (!ERROR) { u_printf("Looking for <E> loops\n"); for (i=1;!ERROR && i<fst2->number_of_graphs+1;i++) { ERROR=look_for_E_loops(i,fst2,graphs_matching_E,ferr); } } clean_controls(fst2,NULL); if (!ERROR) { u_printf("Looking for infinite recursions\n"); for (i=1;!ERROR && i<fst2->number_of_graphs+1;i++) { ERROR=look_for_recursion(i,NULL,fst2,graphs_matching_E,ferr); } } for (i=1;i<fst2->number_of_graphs+1;i++) { free_ConditionList(conditions[i]); } free_abstract_Fst2(fst2,&fst2_free); u_printf("Recursion detection completed\n"); free(conditions); free(graphs_matching_E); if (ERROR) return LEFT_RECURSION; return NO_LEFT_RECURSION; }
/** * Returns 1 if we can match <E> from the current state, with or without * conditions; 0 otherwise. */ int graph_matches_E(int initial_state,int current_state,const Fst2State* states,Fst2Tag* tags, int current_graph,unichar** graph_names, ConditionList conditions_for_states[], ConditionList *graph_conditions) { Transition* l; Fst2State s; int ret_value=0; int ret; *graph_conditions=NULL; s=states[current_state]; if (is_final_state(s)) { /* If we are arrived in a final state, then the graph matches <E> */ set_bit_mask(&(s->control),UNCONDITIONAL_E_MATCH); return 1; } if (is_bit_mask_set(s->control,TMP_LOOP_MARK)) { /* If we have a loop, we do nothing, because they will be * dealt with later. */ return 0; } if (is_bit_mask_set(s->control,VISITED_MARK)) { /* If we are in state that has already been visited */ if (is_bit_mask_set(s->control,UNCONDITIONAL_E_MATCH)) { /* If this state can match <E> without conditions, then we have finished */ return 1; } if (is_bit_mask_set(s->control,CONDITIONAL_E_MATCH)) { /* If this state can match <E> with conditions, then we have finished, but * we copy the necessary conditions in 'graph_conditions'. */ *graph_conditions=clone_ConditionList(conditions_for_states[current_state-initial_state]); return 1; } /* If the state has been visited and if it does not match <E>, then we return OK */ return 0; } set_bit_mask(&(s->control),VISITED_MARK); set_bit_mask(&(s->control),TMP_LOOP_MARK); l=s->transitions; /* We look all the outgoing transitions */ while (l!=NULL) { if (l->tag_number<0) { /* If we have a subgraph, we test if it matches <E> */ *graph_conditions=NULL; ret=graph_matches_E(initial_state,l->state_number,states,tags,current_graph,graph_names,conditions_for_states,graph_conditions); if (ret==1) { /* If the subgraph matches <E>, we say that the current state matches * <E>, modulo the conditions to be verified */ set_bit_mask(&(s->control),CONDITIONAL_E_MATCH); /* We insert the new condition in first position... */ insert_graph_in_conditions(-(l->tag_number),graph_conditions); /* ...and we merge the new conditions with the existing ones for this state */ merge_condition_lists(&conditions_for_states[current_state-initial_state],*graph_conditions); *graph_conditions=NULL; } ret_value=ret_value|ret; } else if (tags[l->tag_number]->control&1) { /* If we have an <E> transition, we explore the rest of the graph from it */ *graph_conditions=NULL; ret=graph_matches_E(initial_state,l->state_number,states,tags,current_graph,graph_names,conditions_for_states,graph_conditions); if (ret==1) { /* If we can match <E> from the <E>-transition's destination state, then * we can match it from the current state. */ if (*graph_conditions==NULL) { /* If there is no condition */ set_bit_mask(&(s->control),UNCONDITIONAL_E_MATCH); } else { /* Otherwise, we add the condition to the existing ones */ set_bit_mask(&(s->control),CONDITIONAL_E_MATCH); merge_condition_lists(&conditions_for_states[current_state-initial_state],*graph_conditions); *graph_conditions=NULL; } } ret_value=ret_value|ret; } l=l->next; } unset_bit_mask(&(s->control),TMP_LOOP_MARK); *graph_conditions=clone_ConditionList(conditions_for_states[current_state-initial_state]); return ret_value; }