#include #include #include /* compile with -DNOFIRST to disable firstset optimization -DFOLLOW_OPT to enable followset optimization NOTE: Followset optimization is not supported when using -s option of LLgen -DDEBUG to print debug information */ extern int LLsymb; extern int LLstartsymb; void LLmessage(int); struct stacks { /* Acces to the stacks is through a 'dynamic array' of pointers * to the heads. We implemented it this way to save on the number * of Malloc() calls. nr_heads is the number of heads; heads_buf_size * is the current size of heads_buf. */ int nr_heads; struct stack_elt **heads_buf; int heads_buf_size; /* r_rec contains nonterminals already tried to build a new * stack with, to prevent right-recursive rules making the * process loop forever */ char r_rec[(LLNNONTERMINALS + 7)/8]; /* join_array contains pointers to already substituted stack * elements, so that if the same nonterminal turns up again * we can make a link */ struct stack_elt *join_array[LLNNONTERMINALS]; /* cleanup_buf contains pointerts to elements that can possibly * be deleted. Again this is implemented as a `growing array'. * Although it's not so clean to do it this way, it DOES save * a lot of time, mainly because much less pointer manipulation * is required, and because it's not necessary to deallocate * the buffer after each turn. Just set nr_cleanups to 0... */ int nr_cleanups; int cleanup_buf_size; struct stack_elt **cleanup_buf; /* visited_buf contains pointers to elements whose flags * need to be cleared */ int nr_visited; int visited_buf_size; struct stack_elt **visited_buf; /* start_seen indicates if the last prediction phase * has matched the start symbol */ int start_seen; /* exp_terminals will contain the terminals that are `on top' * of the prediction graph after a prediction phase */ char exp_terminals[LLSETSIZE]; /* check_run_ok indicates whether a stack element can be deleted * or not */ int check_run_ok; }; #ifdef DEBUG static int allocates = 0; static int deallocates = 0; static int max_in_use = 0; static int edge_allocates = 0; static int edge_deallocates = 0; static int edge_max_in_use = 0; #endif static int grammar_index = 0; /* The grammar should only be build the first time we enter the * recovery routine. grammar_read == 0 indicates this has not * been done yet */ static int grammar_read = 0; /* 'terminals' is an array indexed by the number of a terminal and links * all rules containing this terminal in the RHS */ static struct terminal *terminals; /* 'nonterminals' is an array indexed by the number of a nonterminal * and contains all rules with this nonterminal in the LHS and links all * rules containing this nonterminal in the RHS */ static struct nonterminal *nonterminals; /* These functions must be called instead of the original functions in * 'malloc.h'. They offer a checking allocation mechanism. */ static char *Malloc(unsigned); static char *Realloc(char*, unsigned); /* These functions build the grammar */ static void init_grammar(void); static void build_grammar(void); static struct lhs *build_rule(void); static struct symbol *build_rhs(struct lhs*); static struct symbol *make_link(struct symbol*); /* These functions operate on the stacks */ static int optimize(struct stacks*, struct symbol*, int); static void read_token(void); static void start_stack(struct stacks*, int, int); static void continuation(struct stacks*, int, int); static struct stack_elt *push_rule(struct stack_elt*, struct symbol*); static void new_head(struct stacks*, struct stack_elt*); static void to_delete(struct stacks*, struct stack_elt*); static void substitute(struct stacks*, struct stack_elt*, int); static int join(struct stacks*, struct stack_elt*, int); static int path(struct stack_elt*, struct stack_elt*); static int part_of_loop(struct stack_elt*); static void generate_heads(struct stacks*, struct stack_elt*, int); static void delete(struct stacks*, struct stack_elt*); static void hyp_run(struct stack_elt*); static void check_run(struct stacks*, struct stack_elt*); static struct stack_elt *split(struct stack_elt*); #ifdef DEBUG static void test(struct stacks*); static void dump_stack(struct stack_elt*, int); #endif static void clear_flags(struct stack_elt*, char); static void clear_gen_flags(struct stacks*); static void match_heads(struct stacks*, int); static void cleanup(struct stacks*); static void initialize(struct stacks*); static void calculate(struct stacks*, int); static void kill_stack(struct stacks *stack); void LLnc_recover(void); static char *Malloc(unsigned size) { char *p; if ((p = malloc(size)) == (char *)0) { fprintf(stderr, "fatal error: out of memory\n"); exit(1); } return p; } static char *Realloc(char *ptr, unsigned size) { char *p; if ((p = realloc(ptr, size)) == (char *)0) { fprintf(stderr, "fatal error: out of memory\n"); exit(1); } return p; } static void init_grammar(void) { /* Allocate and initialize an array for terminals and nonterminals */ int i; terminals = (struct terminal *) Malloc((unsigned) LLFIRST_NT * sizeof(struct terminal)); for (i = 0; i < LLFIRST_NT; i++) { (terminals + i)->link = (struct symbol *)0; } nonterminals = (struct nonterminal *) Malloc((unsigned)LLNNONTERMINALS * sizeof(struct nonterminal)); for (i = 0; i < LLNNONTERMINALS; i++) { (nonterminals + i)->rule = (struct lhs *)0; (nonterminals + i)->link = (struct symbol *)0; } } static void build_grammar(void) { /* Build a rule for every nonterminal. The LHS must be saved first because * of the fact that the right side of an assignment statement (in C) will * be evaluated before the left side */ int nt, j; init_grammar(); for (j = 0; j < LLNNONTERMINALS; j++) { nt = LLgrammar[grammar_index]; (nonterminals + nt - LLFIRST_NT)->rule = build_rule(); } } static struct lhs *build_rule(void) { /* Build LHS and call a funcion to create RHS */ struct lhs *l; int j; l = (struct lhs *)Malloc(sizeof(struct lhs)); l->nr = LLgrammar[grammar_index++]; /* Build first set */ for (j = 0; j < LLSETSIZE; j++) { l->first[j] = LLgrammar[grammar_index++]; } /* Build follow set */ for (j = 0; j < LLSETSIZE; j++) { l->follow[j] = LLgrammar[grammar_index++]; } l->empty = LLgrammar[grammar_index++]; /* Can NT produce empty? */ l->rhs = build_rhs(l); return l; } static struct symbol *build_rhs(struct lhs *l) { /* Build RHS by creating structs for all symbols including ALT and * EORULE. Also call a function for linking same terminals and * nonterminals. */ struct symbol *r; r = (struct symbol *)Malloc(sizeof(struct symbol)); if (LLgrammar[grammar_index] == LLALT) { grammar_index++; r->x = LLALT; r->nr = -1; /* Not applicable */ r->link = (struct symbol *)0; /* Not applicable */ r->next = build_rhs(l); r->lhs = l; } else if (LLgrammar[grammar_index] == LLEORULE) { grammar_index++; r->x = LLEORULE; r->nr = -1; /* Not applicable */ r->link = (struct symbol *)0; /* Not applicable */ r->next = (struct symbol *)0; /* Not applicable */ r->lhs = l; } else if (LLgrammar[grammar_index] < LLFIRST_NT) { r->x = LLTERMINAL; r->nr = LLgrammar[grammar_index++]; r->link = make_link(r); r->next = build_rhs(l); r->lhs = l; } else { r->x = LLNONTERMINAL; r->nr = LLgrammar[grammar_index++]; r->link = make_link(r); r->next = build_rhs(l); r->lhs = l; } return r; } static struct symbol *make_link(struct symbol *r) { /* Link same terminals and nonterminals. Every new symbol is appended * in front of the corresponding list for efficiency. */ struct symbol *tmp; if (r->nr < LLFIRST_NT) { /* Terminal array */ tmp = (terminals + r->nr)->link; (terminals + r->nr)->link = r; } else { /* Nonterminal array */ tmp = (nonterminals + (r->nr - LLFIRST_NT))->link; (nonterminals + (r->nr - LLFIRST_NT))->link = r; } return tmp; } /*****************************************************************************/ static int optimize(struct stacks* stack, struct symbol *symb_ptr, int l_ahead) /* Return 1 if rule symb_ptr can start with terminal l_ahead, else return 0. * The array with expected terminals will also be filled in. */ { struct lhs *l; int i; #ifdef NOFIRST return(1); #else if ((l_ahead <= 0) || (l_ahead == EOFILE)) return 1; switch(symb_ptr->x) { case LLTERMINAL: LLPUTIN(stack->exp_terminals, LLindex[symb_ptr->nr]); if (symb_ptr->nr != l_ahead) return 0; else return 1;/*???*/ case LLNONTERMINAL: l = (nonterminals + symb_ptr->nr - LLFIRST_NT)->rule; if (LLIN(l->first, LLindex[l_ahead])) return 1; else if (l->empty) { /* Try next symbol */ return optimize(stack, symb_ptr->next, l_ahead); } else { for (i = 0; i < LLSETSIZE; i++) { stack->exp_terminals[i] |= (l->first)[i]; } return 0; } default: #ifndef FOLLOW_OPT return(1); #else l = (nonterminals + symb_ptr->lhs->nr - LLFIRST_NT)->rule; if (LLIN(l->follow, LLindex[l_ahead])) return 1; else { for (i = 0; i < LLSETSIZE; i++) { stack->exp_terminals[i] |= (l->follow)[i]; } return 0; } #endif /*FOLLOW_OPT */ } #endif /* NOFIRST */ } static void read_token(void) /* Read token and put it in global variable LLsymb, skipping * invalid tokens */ { LLsymb = LL_LEXI(); while (LLindex[LLsymb] < 0) { /* Skip garbage tokens */ LLmessage(0); LLsymb = LL_LEXI(); } } static void start_stack(struct stacks *stack, int base, int l_ahead) /* Start stack on base symbol base with lookahead l_ahead */ { struct stack_elt *bottom, *top; struct symbol *symb_ptr; int i; /* Find first applicable rule */ symb_ptr = (terminals + base)->link; /* Now try all applicable rules */ while (symb_ptr != (struct symbol *)0) { /* If the current rule cannot start with l_ahead, * try the next one */ if (!optimize(stack, symb_ptr->next, l_ahead)) { symb_ptr = symb_ptr->link; continue; } if ( (symb_ptr->next->x == LLTERMINAL) || (symb_ptr->next->x == LLNONTERMINAL) ) { /* Allocate an end-of-stack */ #ifdef DEBUG allocates++; if (allocates - deallocates > max_in_use) { max_in_use = allocates - deallocates; } #endif bottom = (struct stack_elt *) Malloc(sizeof(struct stack_elt)); bottom->edges = (struct edge *)0; bottom->nr = LLEOSTACK; bottom->flags = 0; bottom->nr_nexts = 0; bottom->ref_count = 0; bottom->hyp_ref_count = -1; /* And use the rule to build a stack on it */ top = push_rule(bottom, symb_ptr->next); /* Remember that we're now trying to match the LHS * of the used rule */ bottom->matched = symb_ptr->lhs->nr; if (top->nr >= LLFIRST_NT) { substitute(stack, top, l_ahead); } else { new_head(stack, top); } /* Perhaps this only has produced an empty stack, in * that case bottom can be deallocated. */ if (bottom->ref_count == 0) { to_delete(stack, bottom); } } else { /* base was the last element of the rule, so we * figure we have matched the LHS of this rule. */ if (symb_ptr->lhs->nr == LLstartsymb) { stack->start_seen = 1; } continuation(stack, symb_ptr->lhs->nr, l_ahead); } symb_ptr = symb_ptr->link; } /* Reinitialize some arrays */ for (i = 0; i < (LLNNONTERMINALS + 7)/8; i++) { stack->r_rec[i] = (char) 0; } for (i = 0; i < LLNNONTERMINALS; i++) { stack->join_array[i] = (struct stack_elt *)0; } /* Delete all HEAD flags */ for (i = 0; i < stack->nr_heads; i++) { (*(stack->heads_buf + i))->flags &= ~LLHEAD; } /* Delete flags turned on by 'generate_heads()' */ clear_gen_flags(stack); /* Try to delete elements on cleanup_buf */ cleanup(stack); } static void continuation(struct stacks *stack, int nt, int l_ahead) /* We have 'eaten' a whole stack, and think we recognized nt. Now look for rules that we can proceed with, ie containing nt in the RHS. Each rule found will be developed untill a terminal is at the top of the stack.*/ { struct symbol *symb_ptr; struct stack_elt *bottom, *top; /* If we've already tried this nt, don't do it again. * Otherwise we may loop forever on right-recursive rules */ if (LLIN(stack->r_rec, nt - LLFIRST_NT)) return; /* Mark that we have looked for a continuation for nt */ LLPUTIN(stack->r_rec, nt - LLFIRST_NT); /* Find first applicable rule */ symb_ptr = (nonterminals + nt - LLFIRST_NT)->link; /* Try all applicable rules */ while (symb_ptr != (struct symbol *)0) { /* If the current rule cannot start with l_ahead, * try the next one */ if (!optimize(stack, symb_ptr->next, l_ahead)) { symb_ptr = symb_ptr->link; continue; } if ( (symb_ptr->next->x == LLTERMINAL) || (symb_ptr->next->x == LLNONTERMINAL) ) { #ifdef DEBUG allocates++; if (allocates - deallocates > max_in_use) { max_in_use = allocates - deallocates; } #endif bottom = (struct stack_elt *) Malloc(sizeof(struct stack_elt)); bottom->edges = (struct edge *)0; bottom->nr = LLEOSTACK; bottom->flags = 0; bottom->nr_nexts = 0; bottom->ref_count = 0; bottom->hyp_ref_count = -1; /* Use the rule to build a stack on bottom */ top = push_rule(bottom, symb_ptr->next); /* Remember that we're now trying to match the LHS * of the used rule */ bottom->matched = symb_ptr->lhs->nr; if (top->nr >= LLFIRST_NT) { substitute(stack, top, l_ahead); } else { new_head(stack, top); } /* Perhaps this only has produced an empty stack, in * that case bottom can be deallocated. */ if (bottom->ref_count == 0) { delete(stack, bottom); } } else { /* Stack is still empty */ if (symb_ptr->lhs->nr == LLstartsymb) { stack->start_seen = 1; } continuation(stack, symb_ptr->lhs->nr, l_ahead); } symb_ptr = symb_ptr->link; } } static struct stack_elt *push_rule(struct stack_elt *element, struct symbol *symb_ptr) /* Append the rule symb_ptr to stack element 'element'. Return a * pointer to the new top of the stack */ { struct stack_elt *se, *top; if ( (symb_ptr->next->x == LLTERMINAL) || (symb_ptr->next->x == LLNONTERMINAL) ) { top = push_rule(element, symb_ptr->next); } else { top = element; } #ifdef DEBUG allocates++; if (allocates - deallocates > max_in_use) { max_in_use = allocates - deallocates; } #endif se = (struct stack_elt *)Malloc(sizeof(struct stack_elt)); se->flags = 0; se->nr = symb_ptr->nr; se->ref_count = 0; se->hyp_ref_count = -1; se->matched = -1; se->nr_nexts = 1; #ifdef DEBUG edge_allocates++; if (edge_allocates - edge_deallocates > edge_max_in_use) { edge_max_in_use = edge_allocates - edge_deallocates; } #endif se->edges = (struct edge *)Malloc(sizeof(struct edge)); se->edges->ptr = top; se->edges->flags = 0; top->ref_count++; return se; } static void new_head(struct stacks *stack, struct stack_elt *ptr) /* Make ptr a head of stack */ { /* Is this already a head?*/ if (ptr->flags & LLHEAD) return; if (stack->heads_buf_size == 0) { stack->heads_buf_size = LLHEADS_BUF_INCR; stack->heads_buf = (struct stack_elt **) Malloc(LLHEADS_BUF_INCR * sizeof(struct stack_elt *)); } else if (stack->nr_heads == stack->heads_buf_size) { /* buffer full? */ stack->heads_buf_size += LLHEADS_BUF_INCR; stack->heads_buf = (struct stack_elt **) Realloc((char *) stack->heads_buf, (unsigned) stack->heads_buf_size * sizeof(struct stack_elt *) ); } *(stack->heads_buf + stack->nr_heads) = ptr; /* Add at the tail */ stack->nr_heads++; /* Increase number of heads */ ptr->flags |= LLHEAD; /* Mark it as a head */ ptr->ref_count++; /* Increase reference count */ LLPUTIN(stack->exp_terminals, LLindex[ptr->nr]); } static void to_delete(struct stacks *stack, struct stack_elt *ptr) /* Remember that ptr has to be deleted */ { int i; #ifdef NOCLEAN return; #endif for (i = 0; i < stack->nr_cleanups; i++) { /* Check if already in buffer */ if (*(stack->cleanup_buf + i) == ptr) return; } if (stack->cleanup_buf_size == 0) { stack->cleanup_buf_size = LLCLEANUP_BUF_INCR; stack->cleanup_buf = (struct stack_elt **) Malloc(LLCLEANUP_BUF_INCR * sizeof(struct stack_elt *)); } else if (stack->nr_cleanups == stack->cleanup_buf_size) { stack->cleanup_buf_size += LLCLEANUP_BUF_INCR; stack->cleanup_buf = (struct stack_elt **) Realloc((char *) stack->cleanup_buf, (unsigned) stack->cleanup_buf_size * sizeof(struct stack_elt *)); } *(stack->cleanup_buf + stack->nr_cleanups) = ptr; stack->nr_cleanups++; } static void substitute(struct stacks *stack, struct stack_elt *top, int l_ahead) /* This function substitutes the NT pointed to by 'top'. 'top' should be a top * of a stack */ { struct symbol *symb_ptr; struct stack_elt *new_top; /* Try to join top NT */ if (join(stack, top, l_ahead)) return; /* Find RHS of the rule of nonterminal 'top->nr' */ symb_ptr = (nonterminals + top->nr - LLFIRST_NT)->rule->rhs; /* Mark top as dummy */ top->flags |= LLDUMMY; while (1) { /* If this an empty production, search down the stack for * terminals */ if ((symb_ptr->x == LLALT) || (symb_ptr->x == LLEORULE)) { generate_heads(stack, top, l_ahead); } /* Skip other empty productions, they have no effect. */ while (symb_ptr->x == LLALT) { symb_ptr = symb_ptr->next; } if (symb_ptr->x == LLEORULE) { /* If there are only empty productions, the NT on top * can be deleted */ if (top->ref_count == 0) { to_delete(stack, top); } return; } /* If this rule can produce 'l_ahead' on the top of the stack * substitute the nonterminal */ if (optimize(stack, symb_ptr, l_ahead)) { new_top = push_rule(top, symb_ptr); /* If the new element on top is a nonterminal * substitute it, else make it a head */ if (new_top->nr >= LLFIRST_NT) { substitute(stack, new_top, l_ahead); } else { new_head(stack, new_top); } } /* Search to next alternative */ while ( (symb_ptr->x == LLTERMINAL) || (symb_ptr->x == LLNONTERMINAL) ) { symb_ptr = symb_ptr->next; } if (symb_ptr->x == LLEORULE) { if (top->ref_count == 0) { to_delete(stack, top); } return; } else { symb_ptr = symb_ptr->next; } } } static int join(struct stacks *stack, struct stack_elt *top, int l_ahead) /* This function tries to connect a NT on top of a stack with another stack, * which has already substituted this NT */ { struct stack_elt *se; int size; if ( (se = stack->join_array[top->nr - LLFIRST_NT]) == (struct stack_elt *)0 ) { stack->join_array[top->nr - LLFIRST_NT] = top; return 0; /* Join not possible */ } else { se->nr_nexts++; /* Increase number of descendants */ #ifdef DEBUG edge_allocates++; if (edge_allocates - edge_deallocates > edge_max_in_use) { edge_max_in_use = edge_allocates - edge_deallocates; } #endif /* Allocate one more pointer to descendants */ size = se->nr_nexts * sizeof(struct edge); se->edges = (struct edge *)Realloc((char *) se->edges, (unsigned) size); /* Link it */ (se->edges + se->nr_nexts - 1)->ptr = top->edges->ptr; (se->edges + se->nr_nexts - 1)->flags = 0; /* The successor of 'top' gets an extra predecessor. * 'top' has always only one successor because the stacks are * constructed in 'depth first' order */ top->edges->ptr->ref_count++; #ifndef NOLOOPS /* If we have made a new loop find all stack elements of this * loop and mark them */ if (path(top->edges->ptr, se)) { (se->edges + se->nr_nexts - 1)->flags |= LLLOOP; (se->edges + se->nr_nexts - 1)->flags |= LLYES; } clear_flags(top->edges->ptr, (LLNO | LLYES)); #endif /* Check if joined NT produces empty */ if ((nonterminals + se->nr - LLFIRST_NT)->rule->empty) { generate_heads(stack, top, l_ahead); } /* Deallocate top symbol */ if (top->ref_count == 0) { to_delete(stack, top); } return 1; } } #ifndef NOLOOPS static int path(struct stack_elt *se1, struct stack_elt *se2) /* If there is a path from se1 to se2 it returns 1 and marks all the paths * betweeen these two points, otherwise it returns 0. The flags LLYES and * LLNO are used for optimization. */ { int i, result = 0; if (se1 == se2) return 1; for (i = 0; i < se1->nr_nexts; i++) { if ( (!((se1->edges + i)->flags & LLNO)) && (!((se1->edges + i)->flags & LLLOOP_SEARCH)) ) { (se1->edges + i)->flags |= LLLOOP_SEARCH; if (path((se1->edges + i)->ptr, se2)) { (se1->edges + i)->flags |= LLLOOP; result = 1; } else { (se1->edges + i)->flags |= LLNO; } (se1->edges + i)->flags &= ~LLLOOP_SEARCH; } (se1->edges + i)->flags |= LLYES; } return result; } static int part_of_loop(struct stack_elt *se) /* Checks if 'se' belongs to a loop */ { int i; for (i = 0; i < se->nr_nexts; i++) { if ((se->edges + i)->flags & LLLOOP) return 1; } return 0; } #endif /* NOLOOPS */ static void generate_heads(struct stacks *stack, struct stack_elt *se, int l_ahead) /* This funcion finds all heads starting at 'se'. */ { int i; struct stack_elt *next_se; for (i = 0; i < se->nr_nexts; i++) { if (!((se->edges + i)->ptr->flags & LLGEN_SEARCH)) { (se->edges + i)->ptr->flags |= LLGEN_SEARCH; next_se = (se->edges + i)->ptr; /* Remember a flag has to be cleared later */ if (stack->visited_buf_size == 0) { stack->visited_buf_size = LL_VIS_INCR; stack->visited_buf = (struct stack_elt **) Malloc(LL_VIS_INCR * sizeof(struct stack_elt *)); } else if (stack->nr_visited == stack->visited_buf_size) { stack->visited_buf_size += LL_VIS_INCR; stack->visited_buf = (struct stack_elt **) Realloc((char *) stack->visited_buf, (unsigned) stack->visited_buf_size * sizeof(struct stack_elt *)); } *(stack->visited_buf + stack->nr_visited) = next_se; stack->nr_visited++; if (next_se->flags & LLDUMMY) { generate_heads(stack, next_se, l_ahead); } else if (next_se->nr == LLEOSTACK) { /* We have matched a nt */ if (next_se->matched == LLstartsymb) { stack->start_seen = 1; } continuation(stack, next_se->matched, l_ahead); if (next_se->ref_count == 0) { to_delete(stack, next_se); } } else if (next_se->nr < LLFIRST_NT) { /* terminal */ new_head(stack, next_se); } else { if (next_se->ref_count > 0) { next_se = split(next_se); } substitute(stack, next_se, l_ahead); } } } } static void delete(struct stacks *stack, struct stack_elt *se) /* This function runs down the stack(s) deleting every element which cannot be * reached anymore. */ { int i; #ifdef NOCLEAN return; #endif if (se->ref_count == 0) { /* Decrease reference counts of all successors */ for (i = 0; i < se->nr_nexts; i++) { if ((se->edges + i)->ptr->ref_count != 0) { (se->edges + i)->ptr->ref_count--; /* Try to delete next element */ delete(stack, (se->edges + i)->ptr); } } /* If this element is saved in the join_array clear it */ if (se->nr >= LLFIRST_NT) { if (stack->join_array[se->nr - LLFIRST_NT] == se) { stack->join_array[se->nr - LLFIRST_NT] = (struct stack_elt *)0; } } #ifdef DEBUG deallocates++; edge_deallocates += se->nr_nexts; #endif free((char *) se->edges); free((char *) se); } #ifndef NOLOOPS /* If this element belongs to a loop try to delete it */ else if (part_of_loop(se)) { /* Do a temporary delete */ hyp_run(se); /* Check it */ stack->check_run_ok = 1; check_run(stack, se); /* If it can be deleted delete it */ if (stack->check_run_ok) { se->ref_count = 0; delete(stack, se); } } #endif } #ifndef NOLOOPS static void hyp_run(struct stack_elt *se) /* This function sets the 'hyp_ref_counts' of all elements of the loop that * 'se' belongs to to the value that 'ref_count' will get when 'se' is * deleted */ { int i; struct stack_elt *next_se; for (i = 0; i < se->nr_nexts; i++) { next_se = (se->edges + i)->ptr; if ( (!((se->edges + i)->flags & LLHYP_SEARCH)) && ((se->edges + i)->flags & LLLOOP) ) { (se->edges + i)->flags |= LLHYP_SEARCH; /* If this element is not yet visited initialize * 'hyp_ref_count' else decrease it by one */ if (next_se->hyp_ref_count == -1) { next_se->hyp_ref_count = next_se->ref_count - 1; } else { next_se->hyp_ref_count--; } /* Continue searching */ hyp_run(next_se); } } } static void check_run(struct stacks *stack, struct stack_elt *se) /* This function checks all 'hyp_ref_counts' that 'hyp_run()' has set. * If one of them is not 0, 'check_run_ok' will be set to 0 indicating * that 'se' cannot be deleted. 'check_run()' also resets all 'hyp_ref_counts' */ { int i; if (se->hyp_ref_count > 0) { stack->check_run_ok = 0; } /* Reset 'hyp_ref_count' */ se->hyp_ref_count = -1; for (i = 0; i < se->nr_nexts; i++) { if ((se->edges + i)->flags & LLHYP_SEARCH) { (se->edges + i)->flags &= ~LLHYP_SEARCH; check_run(stack, (se->edges + i)->ptr); } } } #endif /* NOLOOPS */ static struct stack_elt *split(struct stack_elt *se) /* This function splits of a NT in de stack, and returns a pointer to it */ { struct stack_elt *new_stack; int i; #ifdef DEBUG allocates++; if (allocates - deallocates > max_in_use) { max_in_use = allocates - deallocates; } #endif new_stack = (struct stack_elt *)Malloc(sizeof(struct stack_elt)); new_stack->flags = 0; /* Used by 'clear_gen_flags()' */ new_stack->nr = se->nr; new_stack->ref_count = 0; /* Copy is new top */ new_stack->hyp_ref_count = -1; new_stack->matched = -1; new_stack->nr_nexts = se->nr_nexts; #ifdef DEBUG edge_allocates++; if (edge_allocates - edge_deallocates > edge_max_in_use) { edge_max_in_use = edge_allocates - edge_deallocates; } #endif new_stack->edges = (struct edge *) Malloc((unsigned)se->nr_nexts * sizeof(struct edge)); /* Copy gets the same successors as the original */ memcpy((char *) new_stack->edges, (char *) se->edges, se->nr_nexts * sizeof(struct edge)); /* Each successor gets a new predecessor */ for (i = 0; i < new_stack->nr_nexts; i++) { (new_stack->edges + i)->ptr->ref_count++; (new_stack->edges + i)->flags = 0; } return new_stack; } #ifdef DEBUG static void test(struct stacks *stack) { struct stack_elt *se; int i; printf("STACKS:\n"); for (i = 0; i < stack->nr_heads; i++) { printf("%2d: ", i + 1); if (*(stack->heads_buf + i) == (struct stack_elt *)0) { printf("NIL\n"); continue; } se = *(stack->heads_buf + i); dump_stack(se, 1); clear_flags(se, PRINT_SEARCH); } } static void dump_stack(struct stack_elt *se, int level) { int i, j; while (se->nr != LLEOSTACK) { if ((se->flags & LLDUMMY) && (se->nr_nexts > 1)) { printf("[%d] <%d,%d,%d>\n", se->nr, se->ref_count, se->hyp_ref_count, se->flags ); for (j = 0; j < se->nr_nexts; j++) { for (i = 1; i <= level; i++) { printf(" "); } printf("%d: ", j + 1); if (!((se->edges + j)->flags & PRINT_SEARCH)) { printf(" (%d) ", (se->edges + j)->flags); (se->edges + j)->flags |= PRINT_SEARCH; dump_stack((se->edges+j)->ptr,level+1); /*clear_flags((se->edges+j)->ptr,PRINT_SEARCH);*/ } else { printf("LOOP\n"); } } return; } else { if (se->flags & LLDUMMY) { printf("[%d] <%d,%d,%d> ", se->nr,se->ref_count, se->hyp_ref_count, se->flags ); } else { printf("%d <%d,%d,%d> ", se->nr, se->ref_count, se->hyp_ref_count, se->flags ); } if (!(se->edges->flags & PRINT_SEARCH)) { printf(" (%d) ", se->edges->flags); se->edges->flags |= PRINT_SEARCH; se = se->edges->ptr; } else { printf("LOOP\n"); return; } } } printf("\n"); } #endif static void clear_flags(struct stack_elt *se, char flag) /* Clears edge flag 'flag' */ { int i; for (i = 0; i < se->nr_nexts; i++) { if ((se->edges + i)->flags & flag) { (se->edges + i)->flags &= ~flag; /* clear flag */ clear_flags((se->edges + i)->ptr, flag); } } } static void clear_gen_flags(struct stacks *stack) { int i; for (i = 0; i < stack->nr_visited; i++) { (*(stack->visited_buf + i))->flags &= ~(LLGEN_SEARCH); } stack->nr_visited = 0; } static void match_heads(struct stacks *stack, int symb) /* Match heads_buf against symb, leaving only matching heads, * whilst deallocating the non-matching stacks */ { int i; int old_nr_heads; struct stack_elt **old_heads_buf; /* Copy the 'old' heads */ old_nr_heads = stack->nr_heads; old_heads_buf = stack->heads_buf; /* Set heads in stack to 0 */ stack->nr_heads = 0; stack->heads_buf_size = 0; stack->heads_buf = (struct stack_elt **) 0; for (i = 0; i < old_nr_heads; i++) { if ((*(old_heads_buf + i))->nr != symb) { /* Does not match? */ (*(old_heads_buf + i))->ref_count--; (*(old_heads_buf + i))->flags &= ~LLHEAD; delete(stack, *(old_heads_buf + i)); } else { /* Matches */ if (stack->heads_buf_size == 0) { stack->heads_buf_size = LLHEADS_BUF_INCR; stack->heads_buf = (struct stack_elt **) Malloc((unsigned)stack->heads_buf_size * sizeof(struct stack_elt *)); } else if (stack->nr_heads == stack->heads_buf_size) { stack->heads_buf_size += LLHEADS_BUF_INCR; stack->heads_buf = (struct stack_elt **) Realloc((char *) stack->heads_buf, (unsigned) stack->heads_buf_size * sizeof(struct stack_elt *)); } *(stack->heads_buf + stack->nr_heads) = *(old_heads_buf + i); stack->nr_heads++; } } free((char *) old_heads_buf); } static void cleanup(struct stacks *stack) /* Deletes all elements in 'cleanup_buf()' */ { int i; for (i = 0; i < stack->nr_cleanups; i++) { delete(stack, *(stack->cleanup_buf + i)); } stack->nr_cleanups = 0; } static void initialize(struct stacks *stack) /* Initializes some variables and arrays */ { int j; stack->nr_heads = 0; stack->heads_buf_size = 0; stack->heads_buf = (struct stack_elt **)0; stack->nr_cleanups = 0; stack->cleanup_buf_size = 0; stack->cleanup_buf = (struct stack_elt **)0; stack->nr_visited = 0; stack->visited_buf_size = 0; stack->visited_buf = (struct stack_elt **)0; for (j = 0; j < (LLNNONTERMINALS + 7)/8; j++) { stack->r_rec[j] = (char) 0; } for (j = 0; j < LLNNONTERMINALS; j++) { stack->join_array[j] = (struct stack_elt *)0; } for (j = 0; j < LLSETSIZE; j++) { stack->exp_terminals[j] = 0; } stack->start_seen = 0; } static void calculate(struct stacks *stack, int l_ahead) /* This function finds all new heads and deletes the old heads */ { int i; int old_nr_heads; struct stack_elt **old_heads_buf; /* Make a copy of the heads */ old_nr_heads = stack->nr_heads; old_heads_buf = stack->heads_buf; stack->nr_heads = 0; stack->heads_buf = (struct stack_elt **) 0; stack->heads_buf_size = 0; for (i = 0; i < old_nr_heads; i++) { /* Find all new heads */ generate_heads(stack, *(old_heads_buf + i), l_ahead); clear_gen_flags(stack); /* Old head can be deleted now */ (*(old_heads_buf + i))->ref_count--; delete(stack, *(old_heads_buf + i)); } cleanup(stack); free((char *) old_heads_buf); /* Reinitialize some things */ for (i = 0; i < (LLNNONTERMINALS + 7)/8; i++) { stack->r_rec[i] = (char) 0; } for (i = 0; i < LLNNONTERMINALS; i++) { stack->join_array[i] = (struct stack_elt *)0; } /* Delete all HEAD flags */ for (i = 0; i < stack->nr_heads; i++) { (*(stack->heads_buf + i))->flags &= ~LLHEAD; } } static void kill_stack(struct stacks *stack) { int i; for (i = 0; i < stack->nr_heads; i++) { (*(stack->heads_buf + i))->ref_count--; delete(stack, *(stack->heads_buf + i)); } } void LLnc_recover(void) /* This function contains the main loop for non correcting syntax error * recovery */ { int j; int base_symb; struct stacks stack; int max_nr_heads; int max_nr_good_heads; initialize(&stack); max_nr_heads = 0; max_nr_good_heads = 0; /* Grammar has to be read only once */ if (!grammar_read) { build_grammar(); grammar_read = 1; } /* Read first token */ read_token(); base_symb = LLsymb; /* Check on end of file */ if ((base_symb <= 0) || (base_symb == EOFILE)) { if ((nonterminals + LLstartsymb - LLFIRST_NT)->rule->empty != 1 ) { LLsymb = EOFILE; LLmessage(0); } kill_stack(&stack); return; } /* Read look ahead token */ read_token(); /* Now search applicable rules and starts the ball rolling */ start_stack(&stack, base_symb, LLsymb); if (stack.nr_heads > max_nr_heads) { max_nr_heads = stack.nr_heads; } /* Only matching heads are needed */ match_heads(&stack, LLsymb); if (stack.nr_heads > max_nr_good_heads) { max_nr_good_heads = stack.nr_heads; } #ifdef DEBUG test(&stack); #endif /* Loop untill end of inputfile */ while ((LLsymb > 0) && (LLsymb != EOFILE)) { /* When entering the loop LLsymb always contains the * symbol that was used as look_ahead to construct the stacks, * or, if optimization is OFF, it contains the symbol with * which the current heads have been matched */ if (stack.nr_heads == 0) { /* No more heads left */ LLmessage(0); /* Restart the whole thing */ initialize(&stack); /* The look-ahead caused the empty stack, don't * use it to start a new one ! */ read_token(); base_symb = LLsymb; /* Check on end of file */ if ((base_symb <= 0) || (base_symb == EOFILE)) { if ((nonterminals + LLstartsymb - LLFIRST_NT)->rule->empty != 1) { LLsymb = EOFILE; LLmessage(0); } kill_stack(&stack); return; } read_token(); start_stack(&stack, base_symb, LLsymb); if (stack.nr_heads > max_nr_heads) { max_nr_heads = stack.nr_heads; } match_heads(&stack, LLsymb); if (stack.nr_heads > max_nr_good_heads) { max_nr_good_heads = stack.nr_heads; } continue; } /* Normal case starts here */ stack.start_seen = 0; for (j = 0; j < LLSETSIZE; j++) { stack.exp_terminals[j] = 0; } /* Read next symbol */ read_token(); /* Generate all new heads and delete old ones */ calculate(&stack, LLsymb); /* Leave out not wanted heads */ if (stack.nr_heads > max_nr_heads) { max_nr_heads = stack.nr_heads; } match_heads(&stack, LLsymb); if (stack.nr_heads > max_nr_good_heads) { max_nr_good_heads = stack.nr_heads; } #ifdef DEBUG test(&stack); #endif } /* End of file reached, check if we have seen a start symbol */ if (stack.start_seen == 1) return; else { LLsymb = EOFILE; LLmessage(0); } kill_stack(&stack); #ifdef DEBUG printf("Maximum number of heads: %d\n", max_nr_heads); printf("Maximum number of good heads: %d\n", max_nr_good_heads); printf("Number of node allocates: %d\n", allocates); printf("Number of node deallocates: %d\n", deallocates); printf("Maximum number of nodes in use: %8d\n", max_in_use); printf("Sizeof(struct stack_elt) = %8d\n", sizeof(struct stack_elt)); printf(" --------x\n"); printf(" %8d\n", max_in_use * sizeof( struct stack_elt)); printf("Number of edge allocates: %d\n", edge_allocates); printf("Number of edge deallocates: %d\n", edge_deallocates); printf("Maximum number of edges in use: %8d\n", edge_max_in_use); printf("Sizeof(struct edge) = %8d\n", sizeof(struct edge)); printf(" --------x\n"); printf(" %8d\n", edge_max_in_use * sizeof(struct edge)); #endif }