home *** CD-ROM | disk | FTP | other *** search
- /* $Id: mkfirst.c,v 1.2 1999/11/04 14:02:23 shields Exp $ */
- /*
- This software is subject to the terms of the IBM Jikes Compiler
- License Agreement available at the following URL:
- http://www.ibm.com/research/jikes.
- Copyright (C) 1983, 1999, International Business Machines Corporation
- and others. All Rights Reserved.
- You must accept the terms of that agreement to use this software.
- */
- static char hostfile[] = __FILE__;
-
- #include <string.h>
- #include "common.h"
- #include "header.h"
-
- #define LEN (PRINT_LINE_SIZE - 4)
- #define NEXT_RULE_SIZE (num_rules + 1)
- #define LAST_RHS_INDEX(rule_no) rules[rule_no + 1].rhs - 1
-
- #define INIT_FIRST(nt) \
- { \
- register int k; \
- for (k = 0; k < term_set_size; k++)\
- first[nt * term_set_size + k] = 0;\
- }
-
- static BOOLEAN is_terminal_rhs(short *rhs_start,
- BOOLEAN *produces_terminals, int rule_no);
- static BOOLEAN is_nullable_rhs(short *rhs_start, int rule_no);
- static void compute_first(int nt);
- static void print_unreachables(void);
- static void print_xref(void);
- static void print_nt_first(void);
- static void print_follow_map(void);
- static short first_map(int root, int tail);
- static void s_first(int root, int tail, int set);
- static void compute_follow(int nt);
- static void quick_sym(short array[], int l, int h);
- static void check_non_terminals(void);
- static void no_rules_produced(void);
- static void nullables_computation(void);
- static void compute_closure(int lhs_symbol);
- static void compute_produces(int symbol);
-
- static struct f_element_type
- {
- short suffix_root,
- suffix_tail,
- link;
- } *first_element;
-
- static struct node **direct_produces;
- static SET_PTR produces;
-
- /****************************************************************************/
- /* TOP, STACK, and INDEX_OF are used for the linear graph algorithm in */
- /* constructing the FIRST, FOLLOW and CLOSURE maps. */
- /* */
- /* LHS_RULE and NEXT_RULE are used in constructing a map from non-terminals */
- /* to the set of rules produced by the non-terminals. */
- /* */
- /* FIRSTis an array used as a hash table to construct */
- /* the mapping from sequence of symbols to their FIRST terminal */
- /* set. A sequence is hashed into a location depending on the */
- /* first symbol in that sequence. */
- /* */
- /* FIRST_ITEM_OF is a map from each rule into the first item */
- /* that it generates. */
- /* */
- /* The following pointers are used to construct a mapping from each symbol */
- /* in the grammar into the set of items denoted by that symbol. I.e., */
- /* */
- /* f(t) := { [A -> x .t y] | A -> x t y is a rule in the grammar } */
- /* */
- /* Since these sets are simply partitions of the set of items, they are kept*/
- /* all in a sequential list in the array NEXT_ITEM. The roots of the lists */
- /* are placed in the arrats T_ITEMS and NT_ITEMS. */
- /****************************************************************************/
- static short *stack,
- *index_of,
-
- *lhs_rule,
- *next_rule,
-
- *first_table,
- *first_item_of,
-
- *next_item,
- *nt_items,
- *nt_list;
-
- static int top;
-
- /*****************************************************************************/
- /* MKFIRST: */
- /*****************************************************************************/
- /* MKFIRST constructs the FIRST and FOLLOW maps, the CLOSURE map, */
- /* ADEQUATE_ITEM and ITEM_TABLE maps and all other basic maps. */
- /*****************************************************************************/
- void mkfirst(void)
- {
- int symbol,
- nt,
- item_no,
- first_of_empty,
- rule_no,
- i;
-
- BOOLEAN end_node;
-
- term_set_size = num_terminals / SIZEOF_BC
- + (num_terminals % SIZEOF_BC ? 1 : 0);
- non_term_set_size = num_non_terminals / SIZEOF_BC
- + (num_non_terminals % SIZEOF_BC ? 1 : 0);
-
- /* allocate various arrays */
-
- lhs_rule = Allocate_short_array(num_non_terminals);
- lhs_rule -= (num_terminals + 1);
-
- next_rule = Allocate_short_array(NEXT_RULE_SIZE);
- first_item_of = Allocate_short_array(NEXT_RULE_SIZE);
- stack = Allocate_short_array(num_non_terminals + 1);
-
- index_of = Allocate_short_array(num_non_terminals);
- index_of -= (num_terminals + 1);
-
- /*********************************************************************/
- /* NT_FIRST is used to construct a mapping from non-terminals to the */
- /* set of terminals taht may appear first in a string derived from */
- /* the non-terminal. */
- /*********************************************************************/
- nt_first = (SET_PTR)
- calloc(num_non_terminals,
- term_set_size * sizeof(BOOLEAN_CELL));
- if (nt_first == NULL)
- nospace(__FILE__, __LINE__);
-
- nt_first -= ((num_terminals + 1) * term_set_size);
-
- next_item = Allocate_short_array(num_items + 1);
-
- nt_items = Allocate_short_array(num_non_terminals);
- nt_items -= (num_terminals + 1);
-
- nt_list = Allocate_short_array(num_non_terminals);
- nt_list -= (num_terminals + 1);
-
- first_element = (struct f_element_type *)
- calloc(num_items + 1, sizeof(struct f_element_type));
- if (first_element == NULL)
- nospace(__FILE__, __LINE__);
-
- item_table = (struct itemtab *)
- calloc(num_items + 1, sizeof(struct itemtab));
- if (item_table == NULL)
- nospace(__FILE__, __LINE__);
-
- for ALL_NON_TERMINALS(i) /* Initialize LHS_RULE to NIL */
- lhs_rule[i] = NIL;
-
- /**************************************************************/
- /* In this loop, we construct the LHS_RULE map which maps */
- /* each non-terminal symbol into the set of rules it produces */
- /**************************************************************/
- for ALL_RULES(rule_no)
- {
- symbol = rules[rule_no].lhs;
- if (lhs_rule[symbol] == NIL)
- next_rule[rule_no] = rule_no;
- else
- {
- next_rule[rule_no] = next_rule[lhs_rule[symbol]];
- next_rule[lhs_rule[symbol]] = rule_no;
- }
- lhs_rule[symbol] = rule_no;
- }
-
- /*************************************************************/
- /* Check if there are any non-terminals that do not produce */
- /* any rules. */
- /*************************************************************/
-
- no_rules_produced();
-
- /*************************************************************/
- /* Construct the CLOSURE map of non-terminals. */
- /*************************************************************/
- closure = (struct node **)
- calloc(num_non_terminals, sizeof(struct node *));
- if (closure == NULL)
- nospace(__FILE__, __LINE__);
- closure -= (num_terminals + 1);
-
- for ALL_NON_TERMINALS(i)
- index_of[i] = OMEGA;
-
- top = 0;
- for ALL_NON_TERMINALS(nt)
- {
- if (index_of[nt] == OMEGA)
- compute_closure(nt);
- }
-
- /*************************************************************/
- /* Construct the NULL_NT map for non-terminals. */
- /* A non-terminal B is said to be nullable if either: */
- /* B -> %empty or B -> B1 B2 B3 ... Bk where Bi is */
- /* nullable for 1 <= i <= k */
- /*************************************************************/
- null_nt = Allocate_boolean_array(num_non_terminals);
- null_nt -= (num_terminals + 1);
-
- nullables_computation();
-
- /*************************************************************/
- /* Construct the FIRST map for non-terminals and also a list */
- /* of non-terminals whose first set is empty. */
- /*************************************************************/
- for ALL_NON_TERMINALS(i) /* Initialize INDEX_OF to OMEGA */
- index_of[i] = OMEGA;
- top = 0;
- for ALL_NON_TERMINALS(nt)
- {
- if (index_of[nt] == OMEGA)
- compute_first(nt);
- }
-
- /*************************************************************/
- /* Since every input source will be followed by the EOFT */
- /* symbol, FIRST[accept_image] cannot contain empty but */
- /* instead must contain the EOFT symbol. */
- /*************************************************************/
- if (null_nt[accept_image])
- {
- null_nt[accept_image] = FALSE;
- RESET_BIT_IN(nt_first, accept_image, empty);
- SET_BIT_IN(nt_first, accept_image, eoft_image);
- }
-
- /***************************************************************/
- /* Check whether there are any non-terminals that do not */
- /* generate any terminal strings. If so, signal error and stop.*/
- /***************************************************************/
-
- check_non_terminals();
-
- /***************************************************************/
- /* Construct the ITEM_TABLE, FIRST_ITEM_OF, and NT_ITEMS maps. */
- /***************************************************************/
-
- first_table = Allocate_short_array(num_symbols + 1);
-
- for ALL_SYMBOLS(i) /* Initialize FIRST_TABLE to NIL */
- first_table[i] = NIL;
-
- top = 1;
- first_of_empty = top;
- first_element[first_of_empty].suffix_root = 1;
- first_element[first_of_empty].suffix_tail = 0;
-
- for ALL_NON_TERMINALS(i) /* Initialize NT_ITEMS to NIL */
- nt_items[i] = NIL;
-
- item_no = 0;
- item_table[item_no].rule_number = 0;
- item_table[item_no].symbol = empty;
- item_table[item_no].dot = 0;
- item_table[item_no].suffix_index = NIL;
-
- for ALL_RULES(rule_no)
- {
- int j,
- k;
-
- first_item_of[rule_no] = item_no + 1;
- j = 0;
- k = LAST_RHS_INDEX(rule_no);
- for ENTIRE_RHS(i, rule_no)
- {
- item_no++;
- symbol = rhs_sym[i];
- item_table[item_no].rule_number = rule_no;
- item_table[item_no].symbol = symbol;
- item_table[item_no].dot = j;
-
- if (lalr_level > 1 ||
- symbol IS_A_NON_TERMINAL ||
- symbol == error_image)
- {
- if (i == k)
- item_table[item_no].suffix_index = first_of_empty;
- else
- item_table[item_no].suffix_index = first_map(i + 1, k);
- }
- else
- item_table[item_no].suffix_index = NIL;
-
- if (symbol IS_A_NON_TERMINAL)
- {
- next_item[item_no] = nt_items[symbol];
- nt_items[symbol] = item_no;
- }
- j++;
- }
-
- item_table[++item_no].rule_number = rule_no;
- item_table[item_no].symbol = empty;
- item_table[item_no].dot = j;
- item_table[item_no].suffix_index = NIL;
- }
-
-
- /***************************************************************/
- /* We now compute the first set for all suffixes that were */
- /* inserted in the FIRST_TABLE map. There are TOP such suffixes*/
- /* Extra space is also allocated to compute the first set for */
- /* suffixes whose left-hand side is the ACCEPT non-terminal. */
- /* The first set for these suffixes are the sets needed to */
- /* construct the FOLLOW map and compute look-ahead sets. They */
- /* are placed in the FIRST table in the range 1..NUM_FIRST_SETS*/
- /* The first element in the FIRST table contains the first sets*/
- /* for the empty sequence. */
- /***************************************************************/
- num_first_sets = top;
-
- for (end_node = ((rule_no = lhs_rule[accept_image]) == NIL);
- ! end_node;
- end_node = (rule_no == lhs_rule[accept_image]))
- {
- rule_no = next_rule[rule_no];
- num_first_sets++;
- }
-
- first = (SET_PTR)
- calloc(num_first_sets + 1, term_set_size * sizeof(BOOLEAN_CELL));
- if (first == NULL)
- nospace(__FILE__, __LINE__);
- for (i = 1; i <= top; i++)
- {
- s_first(first_element[i].suffix_root,
- first_element[i].suffix_tail, i);
- }
-
- rule_no = lhs_rule[accept_image];
- for (i = top + 1; i <= num_first_sets; i++)
- {
- rule_no = next_rule[rule_no];
- item_no = first_item_of[rule_no];
- item_table[item_no].suffix_index = i;
- INIT_FIRST(i);
- SET_BIT_IN(first, i, eoft_image);
- }
-
- /***************************************************************/
- /* If the READ/REDUCE option is on, we precalculate the kernel */
- /* of the final states which simply consists of the last item */
- /* in the corresponding rule. Rules with the ACCEPT */
- /* non-terminal as their left-hand side are not considered so */
- /* as to let the Accpet action remain as a Reduce action */
- /* instead of a Goto/Reduce action. */
- /***************************************************************/
- adequate_item = (struct node **)
- calloc(num_rules + 1, sizeof(struct node *));
- if (adequate_item == NULL)
- nospace(__FILE__, __LINE__);
-
- if (read_reduce_bit)
- {
- for ALL_RULES(rule_no)
- {
- int j;
-
- j = RHS_SIZE(rule_no);
- if (rules[rule_no].lhs != accept_image && j > 0)
- {
- struct node *p;
-
- item_no = first_item_of[rule_no] + j;
- p = Allocate_node();
- p -> value = item_no;
- p -> next = NULL;
- adequate_item[rule_no] = p;
- }
- else
- adequate_item[rule_no] = NULL;
- }
- }
-
-
- /***************************************************************/
- /* Construct the CLITEMS map. Each element of CLITEMS points */
- /* to a circular linked list of items. */
- /***************************************************************/
- clitems = (struct node **)
- calloc(num_non_terminals, sizeof(struct node *));
- if (clitems == NULL)
- nospace(__FILE__, __LINE__);
- clitems -= (num_terminals + 1);
-
- for ALL_NON_TERMINALS(nt)
- {
- clitems[nt] = NULL;
-
- for (end_node = ((rule_no = lhs_rule[nt]) == NIL);
- ! end_node;
- end_node = (rule_no == lhs_rule[nt]))
- {
- struct node *p;
-
- rule_no = next_rule[rule_no];
- p = Allocate_node();
- p -> value = first_item_of[rule_no];
- if (clitems[nt] == NULL)
- p -> next = p;
- else
- {
- p -> next = clitems[nt] -> next;
- clitems[nt] -> next = p;
- }
- clitems[nt] = p;
- }
- }
-
- /***************************************************************/
- /* If LALR_LEVEL > 1, we need to calculate RMPSELF, a set that */
- /* identifies the nonterminals that can right-most produce */
- /* themselves. In order to compute RMPSELF, the map PRODUCES */
- /* must be constructed which identifies for each nonterminal */
- /* the set of nonterminals that it can right-most produce. */
- /***************************************************************/
- if (lalr_level > 1)
- {
- produces = (SET_PTR)
- calloc(num_non_terminals,
- non_term_set_size * sizeof(BOOLEAN_CELL));
- if (produces == NULL)
- nospace(__FILE__, __LINE__);
- produces -= ((num_terminals + 1) * non_term_set_size);
-
- direct_produces = (struct node **)
- calloc(num_non_terminals, sizeof(struct node *));
- if (direct_produces == NULL)
- nospace(__FILE__, __LINE__);
- direct_produces -= (num_terminals + 1);
-
- for ALL_NON_TERMINALS(nt)
- {
- struct node *p,
- *q;
-
- for (end_node = ((p = clitems[nt]) == NULL);
- ! end_node; end_node = (p == clitems[nt]))
- {
- p = p -> next;
- item_no = p -> value;
- symbol = item_table[item_no].symbol;
- if (symbol IS_A_NON_TERMINAL)
- {
- i = item_table[item_no].suffix_index;
- if (IS_IN_SET(first, i, empty) &&
- (! IS_IN_NTSET(produces, nt,
- symbol - num_terminals)))
- {
- NTSET_BIT_IN(produces, nt,
- symbol - num_terminals);
- q = Allocate_node();
- q -> value = symbol;
- q -> next = direct_produces[nt];
- direct_produces[nt] = q;
- }
- }
- }
- }
-
- /************************************************************/
- /* Complete the construction of the RIGHT_MOST_PRODUCES map */
- /* for non-terminals using the digraph algorithm. */
- /************************************************************/
- for ALL_NON_TERMINALS(nt)
- index_of[nt] = OMEGA;
-
- top = 0;
- for ALL_NON_TERMINALS(nt)
- {
- if (index_of[nt] == OMEGA)
- compute_produces(nt);
- }
-
- init_rmpself(produces);
-
- produces += ((num_terminals + 1) * non_term_set_size);
- ffree(produces);
- direct_produces += (num_terminals + 1);
- ffree(direct_produces);
- }
-
- /***************************************************************/
- /* Construct the FOLLOW map if */
- /* 1) an SLR table is requested */
- /* 2) if we have to print the FOLLOW map */
- /* 3) Error-maps are requested */
- /* 4) There are more than one starting symbol. */
- /***************************************************************/
- if (slr_bit || follow_bit || error_maps_bit ||
- next_rule[lhs_rule[accept_image]] != lhs_rule[accept_image])
- {
- follow = (SET_PTR)
- calloc(num_non_terminals,
- term_set_size * sizeof(BOOLEAN_CELL));
- if (follow == NULL)
- nospace(__FILE__, __LINE__);
- follow -= ((num_terminals + 1) * term_set_size);
-
- SET_BIT_IN(follow, accept_image, eoft_image);
-
- for ALL_NON_TERMINALS(i) /* Initialize INDEX_OF to OMEGA */
- index_of[i] = OMEGA;
-
- index_of[accept_image] = INFINITY; /* mark computed */
- top = 0;
-
- for ALL_NON_TERMINALS(nt)
- {
- if (index_of[nt] == OMEGA) /* not yet computed ? */
- compute_follow(nt);
- }
-
- /***************************************************************/
- /* Initialize FIRST for suffixes that can follow each starting*/
- /* non-terminal ( except the main symbol) with the FOLLOW set */
- /* of the non-terminal in question. */
- /***************************************************************/
- rule_no = lhs_rule[accept_image];
- if (next_rule[rule_no] != rule_no)
- {
- rule_no = next_rule[rule_no]; /* first rule */
- top = item_table[first_item_of[rule_no]].suffix_index;
- for (i = top + 1; i <= num_first_sets; i++)
- {
- rule_no = next_rule[rule_no];
- item_no = first_item_of[rule_no];
- symbol = item_table[item_no].symbol;
- if (symbol IS_A_NON_TERMINAL)
- {
- ASSIGN_SET(first, i, follow, symbol);
- }
- }
- }
- }
-
- /***************************************************************/
- /* If WARNINGS option is turned on, the unreachable symbols in */
- /* the grammar are printed. */
- /***************************************************************/
- if (warnings_bit)
- print_unreachables();
-
- /***************************************************************/
- /* If a Cross_Reference listing is requested, it is generated */
- /* here. */
- /***************************************************************/
- if (xref_bit)
- print_xref();
-
- /***************************************************************/
- /* If a listing of the FIRST map is requested, it is generated */
- /* here. */
- /***************************************************************/
- if (first_bit)
- print_nt_first();
-
- /****************************************************************/
- /* If a listing of the FOLLOW map is requested, it is generated */
- /* here. */
- /***************************************************************/
- if (follow_bit)
- print_follow_map();
-
- /***************************************************************/
- /* Free allocated arrays. */
- /***************************************************************/
- nt_first += ((num_terminals + 1) * term_set_size);
- ffree(nt_first);
- nt_list += (num_terminals + 1);
- ffree(nt_list);
- ffree(first_table);
- ffree(first_element);
- nt_items += (num_terminals + 1);
- ffree(nt_items);
- ffree(next_item);
- ffree(stack);
- index_of += (num_terminals + 1);
- ffree(index_of);
- lhs_rule += (num_terminals + 1);
- ffree(lhs_rule);
- ffree(next_rule);
- ffree(first_item_of);
-
- return;
- }
-
-
- /*****************************************************************************/
- /* NO_RULES_PRODUCED: */
- /*****************************************************************************/
- static void no_rules_produced(void)
- {
- char line[PRINT_LINE_SIZE + 1],
- tok[SYMBOL_SIZE + 1];
-
- int nt_root,
- nt_last,
- symbol;
-
- /*************************************************************/
- /* Build a list of all non-terminals that do not produce any */
- /* rules. */
- /*************************************************************/
- nt_root = NIL;
- for ALL_NON_TERMINALS(symbol)
- {
- if (lhs_rule[symbol] == NIL)
- {
- if (nt_root == NIL)
- nt_root = symbol;
- else
- nt_list[nt_last] = symbol;
- nt_last = symbol;
- }
- }
-
- /*************************************************************/
- /* If the list of non-terminals that do not produce any rules*/
- /* is not empty, signal error and stop. */
- /*************************************************************/
-
- if (nt_root != NIL)
- {
- PR_HEADING;
- nt_list[nt_last] = NIL;
- if (nt_list[nt_root] == NIL)
- {
- PRNTERR("The following Non-terminal does not produce any rules: ");
- }
- else
- {
- PRNTERR("The following Non-terminals do not produce any rules: ");
- }
- strcpy(line, " ");
-
- for (symbol = nt_root; symbol != NIL; symbol = nt_list[symbol])
- {
- restore_symbol(tok, RETRIEVE_STRING(symbol));
- if (strlen(line) + strlen(tok) > PRINT_LINE_SIZE)
- {
- PRNT(line);
- print_large_token(line, tok, " ", LEN);
- }
- else
- strcat(line, tok);
- strcat(line,BLANK);
- }
- PRNT(line);
- exit(12);
- }
- }
-
-
- /*****************************************************************************/
- /* COMPUTE_CLOSURE: */
- /*****************************************************************************/
- /* This function computes the closure of a non-terminal LHS_SYMBOL passed */
- /* to it as an argument using the digraph algorithm. */
- /* The closure of a non-terminal A is the set of all non-terminals Bi that */
- /* can directly or indirectly start a string generated by A. */
- /* I.e., A *::= Bi X where X is an arbitrary string. */
- /*****************************************************************************/
- static void compute_closure(int lhs_symbol)
- {
- int indx;
- short *nont_list;
-
- int symbol,
- rule_no,
- nt_root,
- i;
-
- struct node *p,
- *q;
-
- BOOLEAN end_node;
-
- nont_list = Allocate_short_array(num_non_terminals);
- nont_list -= (num_terminals + 1); /* Temporary direct */
- /* access set for closure. */
- stack[++top] = lhs_symbol;
- indx = top;
- index_of[lhs_symbol] = indx;
-
- for ALL_NON_TERMINALS(i)
- nont_list[i] = OMEGA;
-
- nont_list[lhs_symbol] = NIL;
- nt_root = lhs_symbol;
-
- closure[lhs_symbol] = NULL; /* Permanent closure set. Linked list */
-
- for (end_node = ((rule_no = lhs_rule[lhs_symbol]) == NIL);
- ! end_node; /* Iterate over all rules of LHS_SYMBOL */
- end_node = (rule_no == lhs_rule[lhs_symbol]))
- {
- rule_no = next_rule[rule_no];
- symbol = (RHS_SIZE(rule_no) == 0 ? empty
- : rhs_sym[rules[rule_no].rhs]);
-
- if (symbol IS_A_NON_TERMINAL)
- {
- if (nont_list[symbol] == OMEGA)
- {
- if (index_of[symbol] == OMEGA) /* if first time seen */
- compute_closure(symbol);
-
- index_of[lhs_symbol] = MIN(index_of[lhs_symbol],
- index_of[symbol]);
- nont_list[symbol] = nt_root;
- nt_root = symbol;
-
- /* add closure[symbol] to closure of LHS_SYMBOL. */
-
- for (end_node = ((q = closure[symbol]) == NULL);
- ! end_node;
- end_node = (q == closure[symbol]))
- {
- q = q -> next;
- if (nont_list[q -> value] == OMEGA)
- {
- nont_list[q -> value] = nt_root;
- nt_root = q -> value;
- }
- }
- }
- }
- }
-
- for (; nt_root != lhs_symbol; nt_root = nont_list[nt_root])
- {
- p = Allocate_node();
- p -> value = nt_root;
- if (closure[lhs_symbol] == NULL)
- p -> next = p;
- else
- {
- p -> next = closure[lhs_symbol] -> next;
- closure[lhs_symbol] -> next = p;
- }
- closure[lhs_symbol] = p;
- }
-
- if (index_of[lhs_symbol] == indx)
- {
- for (symbol = stack[top]; symbol != lhs_symbol; symbol = stack[--top])
- {
- q = closure[symbol];
- if (q != NULL)
- {
- p = q -> next;
- free_nodes(p, q); /* free nodes used by CLOSURE[SYMBOL] */
- closure[symbol] = NULL;
- }
-
- p = Allocate_node();
- p -> value = lhs_symbol;
- p -> next = p;
- closure[symbol] = p;
-
- for (end_node = ((q = closure[lhs_symbol]) == NULL);
- ! end_node;
- end_node = (q == closure[lhs_symbol]))
- {
- q = q -> next;
- if (q -> value != symbol)
- {
- p = Allocate_node();
- p -> value = q -> value;
- p -> next = closure[symbol] -> next;
- closure[symbol] -> next = p;
- closure[symbol] = p;
- }
- }
-
- index_of[symbol] = INFINITY;
- }
-
- index_of[lhs_symbol] = INFINITY;
- top--;
- }
-
- nont_list += (num_terminals + 1);
- ffree(nont_list);
-
- return;
- }
-
-
- /*****************************************************************************/
- /* NULLABLES_COMPUTATION: */
- /*****************************************************************************/
- /* This procedure computes the set of non-terminal symbols that can */
- /* generate the empty string. Such non-terminals are said to be nullable. */
- /* */
- /* A non-terminal "A" can generate empty if the grammar in question contains */
- /* a rule: */
- /* A ::= B1 B2 ... Bn n >= 0, 1 <= i <= n */
- /* and Bi, for all i, is a nullable non-terminal. */
- /*****************************************************************************/
- static void nullables_computation(void)
- {
- short *rhs_start;
-
- int rule_no,
- nt;
-
- BOOLEAN changed = TRUE,
- end_node;
-
- rhs_start = Allocate_short_array(NEXT_RULE_SIZE);
-
- /******************************************************************/
- /* First, mark all non-terminals as non-nullable. Then initialize*/
- /* RHS_START. RHS_START is a mapping from each rule in the grammar*/
- /* into the next symbol in its right-hand side that has not yet */
- /* proven to be nullable. */
- /******************************************************************/
- for ALL_NON_TERMINALS(nt)
- null_nt[nt] = FALSE;
-
- for ALL_RULES(rule_no)
- rhs_start[rule_no] = rules[rule_no].rhs;
-
- /******************************************************************/
- /* We now iterate over the rules and try to advance the RHS_START */
- /* pointer thru each right-hand side as far as we can. If one or */
- /* more non-terminals are found to be nullable, they are marked */
- /* as such and the process is repeated. */
- /* */
- /* If we go through all the rules and no new non-terminal is found*/
- /* to be nullable then we stop and return. */
- /* */
- /* Note that for each iteration, only rules associated with */
- /* non-terminals that are non-nullable are considered. Further, */
- /* as soon as a non-terminal is found to be nullable, the */
- /* remaining rules associated with it are not considered. I.e., */
- /* we quit the inner loop. */
- /******************************************************************/
- while(changed)
- {
- changed = FALSE;
-
- for ALL_NON_TERMINALS(nt)
- {
- for (end_node = ((rule_no = lhs_rule[nt]) == NIL);
- ! null_nt[nt] && ! end_node;
- end_node = (rule_no == lhs_rule[nt]))
- {
- rule_no = next_rule[rule_no];
- if (is_nullable_rhs(rhs_start,rule_no))
- {
- changed = TRUE;
- null_nt[nt] = TRUE;
- }
- }
- }
- }
-
- ffree(rhs_start);
-
- return;
- }
-
-
- /*****************************************************************************/
- /* IS_NULLABLE_RHS: */
- /*****************************************************************************/
- /* This procedure tries to advance the RHS_START pointer. If the current */
- /* symbol identified by the RHS_START element is a terminal it returns FALSE */
- /* to indicate that it cannot go any further. If it encounters a non-null- */
- /* lable non-terminal, it also returns FALSE. Otherwise, the whole right-hand*/
- /* side is consumed, and it returns the value TRUE. */
- /*****************************************************************************/
- static BOOLEAN is_nullable_rhs(short *rhs_start, int rule_no)
- {
- int symbol;
-
- for(rhs_start[rule_no] = rhs_start[rule_no];
- rhs_start[rule_no] <= rules[rule_no + 1].rhs - 1;
- rhs_start[rule_no]++)
- {
- symbol = rhs_sym[rhs_start[rule_no]];
- if (symbol IS_A_TERMINAL)
- return(FALSE);
- else if (! null_nt[symbol]) /* symbol is a non-terminal */
- return(FALSE);
- }
-
- return(TRUE);
- }
-
-
- /*****************************************************************************/
- /* COMPUTE_FIRST: */
- /*****************************************************************************/
- /* This subroutine computes FIRST(NT) for some non-terminal NT using the */
- /* digraph algorithm. */
- /* FIRST(NT) is the set of all terminals Ti that may start a string generated*/
- /* by NT. That is, NT *::= Ti X where X is an arbitrary string. */
- /*****************************************************************************/
- static void compute_first(int nt)
- {
- int indx;
-
- BOOLEAN end_node,
- blocked;
-
- int i,
- symbol,
- rule_no;
-
- SET_PTR temp_set;
-
- temp_set = (SET_PTR)
- calloc(1, term_set_size * sizeof(BOOLEAN_CELL));
- if (temp_set == NULL)
- nospace(__FILE__, __LINE__);
-
- stack[++top] = nt;
- indx = top;
- index_of[nt] = indx;
-
- /**************************************************************/
- /* Iterate over all rules generated by non-terminal NT... */
- /* In this application of the transitive closure algorithm, */
- /* */
- /* G(A) := { t | A ::= t X for a terminal t and a string X } */
- /* */
- /* The relation R is defined as follows: */
- /* */
- /* R(A, B) iff A ::= B1 B2 ... Bk B X */
- /* */
- /* where Bi is nullable for 1 <= i <= k */
- /**************************************************************/
-
- for (end_node = ((rule_no = lhs_rule[nt]) == NIL);
- ! end_node; /* Iterate over all rules produced by NT */
- end_node = (rule_no == lhs_rule[nt]))
- {
- rule_no = next_rule[rule_no];
- blocked = FALSE;
-
- for ENTIRE_RHS(i, rule_no)
- {
- symbol = rhs_sym[i];
- if (symbol IS_A_NON_TERMINAL)
- {
- if (index_of[symbol] == OMEGA)
- compute_first(symbol);
- index_of[nt] = MIN( index_of[nt], index_of[symbol]);
-
- ASSIGN_SET(temp_set, 0, nt_first, symbol);
- RESET_BIT(temp_set, empty);
- SET_UNION(nt_first, nt, temp_set, 0);
- blocked = NOT(null_nt[symbol]);
- }
- else
- {
- SET_BIT_IN(nt_first, nt, symbol);
- blocked = TRUE;
- }
-
- if (blocked)
- break;
- }
-
- if (! blocked)
- {
- SET_BIT_IN(nt_first, nt, empty);
- }
- }
-
- if (index_of[nt] == indx)
- {
- for (symbol = stack[top]; symbol != nt; symbol = stack[--top])
- {
- ASSIGN_SET(nt_first, symbol, nt_first, nt);
- index_of[symbol] = INFINITY;
- }
- index_of[nt] = INFINITY;
- top--;
- }
- ffree(temp_set);
- return;
- }
-
-
- /*****************************************************************************/
- /* CHECK_NON_TERMINALS: */
- /*****************************************************************************/
- /* This procedure checks whether or not any non-terminal symbols can fail to */
- /* generate a string of terminals. */
- /* */
- /* A non-terminal "A" can generate a terminal string if the grammar in */
- /* question contains a rule of the form: */
- /* */
- /* A ::= X1 X2 ... Xn n >= 0, 1 <= i <= n */
- /* */
- /* and Xi, for all i, is a terminal or a non-terminal that can generate a */
- /* string of terminals. */
- /* This routine is structurally identical to COMPUTE_NULLABLES. */
- /*****************************************************************************/
- static void check_non_terminals(void)
- {
- char line[PRINT_LINE_SIZE + 1],
- tok[SYMBOL_SIZE + 1];
-
- short *rhs_start;
-
- int rule_no,
- nt_root,
- nt_last,
- symbol,
- nt;
-
- BOOLEAN changed = TRUE,
- end_node,
- *produces_terminals;
-
- rhs_start = Allocate_short_array(NEXT_RULE_SIZE);
- produces_terminals = Allocate_boolean_array(num_non_terminals);
- produces_terminals -= (num_terminals + 1);
-
- /******************************************************************/
- /* First, mark all non-terminals as not producing terminals. Then */
- /* initialize RHS_START. RHS_START is a mapping from each rule in */
- /* the grammar into the next symbol in its right-hand side that */
- /* has not yet proven to be a symbol that generates terminals. */
- /******************************************************************/
- for ALL_NON_TERMINALS(nt)
- produces_terminals[nt] = FALSE;
-
- produces_terminals[accept_image] = TRUE;
-
- for ALL_RULES(rule_no)
- rhs_start[rule_no] = rules[rule_no].rhs;
-
- /******************************************************************/
- /* We now iterate over the rules and try to advance the RHS_START */
- /* pointer to each right-hand side as far as we can. If one or */
- /* more non-terminals are found to be "all right", they are */
- /* marked as such and the process is repeated. */
- /* */
- /* If we go through all the rules and no new non-terminal is */
- /* found to be "all right" then we stop and return. */
- /* */
- /* Note that on each iteration, only rules associated with */
- /* non-terminals that are not "all right" are considered. Further,*/
- /* as soon as a non-terminal is found to be "all right", the */
- /* remaining rules associated with it are not considered. I.e., */
- /* we quit the inner loop. */
- /******************************************************************/
- while(changed)
- {
- changed = FALSE;
-
- for ALL_NON_TERMINALS(nt)
- {
- for (end_node = ((rule_no = lhs_rule[nt]) == NIL);
- (! produces_terminals[nt]) && (! end_node);
- end_node = (rule_no == lhs_rule[nt]))
- {
- rule_no = next_rule[rule_no];
- if (is_terminal_rhs(rhs_start, produces_terminals, rule_no))
- {
- changed = TRUE;
- produces_terminals[nt] = TRUE;
- }
- }
- }
- }
-
- /*************************************************************/
- /* Construct a list of all non-terminals that do not generate*/
- /* terminal strings. */
- /*************************************************************/
- nt_root = NIL;
- for ALL_NON_TERMINALS(nt)
- {
- if (! produces_terminals[nt])
- {
- if (nt_root == NIL)
- nt_root = nt;
- else
- nt_list[nt_last] = nt;
- nt_last = nt;
- }
- }
-
- /*************************************************************/
- /* If there are non-terminal symbols that do not generate */
- /* terminal strings, print them out and stop the program. */
- /*************************************************************/
- if (nt_root != NIL)
- {
- nt_list[nt_last] = NIL; /* mark end of list */
- PR_HEADING;
- strcpy(line, "*** ERROR: The following Non-terminal");
- if (nt_list[nt_root] == NIL)
- strcat(line, " does not generate any terminal strings: ");
- else
- {
- strcat(line, "s do not generate any terminal strings: ");
- PRNT(line);
- strcpy(line, " "); /* 8 spaces */
- }
-
- for (symbol = nt_root; symbol != NIL; symbol = nt_list[symbol])
- {
- restore_symbol(tok, RETRIEVE_STRING(symbol));
- if (strlen(line) + strlen(tok) > PRINT_LINE_SIZE-1)
- {
- PRNT(line);
- print_large_token(line, tok, " ", LEN);
- }
- else
- strcat(line, tok);
- strcat(line, BLANK);
- }
- PRNT(line);
- exit(12);
- }
- produces_terminals += (num_terminals + 1);
- ffree(produces_terminals);
- ffree(rhs_start);
- }
-
-
- /*****************************************************************************/
- /* IS_TERMINAL_RHS: */
- /*****************************************************************************/
- /* This procedure tries to advance the RHS_START pointer. If the current */
- /* symbol identified by the RHS_START element is a bad non-terminal it */
- /* returns FALSE. Otherwise, the whole right-hand side is traversed, and it */
- /* returns the value TRUE. */
- /*****************************************************************************/
- static BOOLEAN is_terminal_rhs(short *rhs_start,
- BOOLEAN *produces_terminals, int rule_no)
- {
- int symbol;
-
- for(rhs_start[rule_no] = rhs_start[rule_no];
- rhs_start[rule_no] <= rules[rule_no + 1].rhs - 1;
- rhs_start[rule_no]++)
- {
- symbol = rhs_sym[rhs_start[rule_no]];
- if (symbol IS_A_NON_TERMINAL)
- {
- if (! produces_terminals[symbol])
- return(FALSE);
- }
- }
-
- return(TRUE);
- }
-
-
- /*****************************************************************************/
- /* FIRST_MAP: */
- /*****************************************************************************/
- /* FIRST_MAP takes as arguments two pointers, ROOT and TAIL, to a sequence */
- /* of symbols in RHS which it inserts in FIRST_TABLE. The vector FIRST_TABLE*/
- /* is used as the base for a hashed table where collisions are resolved by */
- /* links. Elements added to this hash table are allocated from the vector */
- /* FIRST_ELEMENT, with the variable TOP always indicating the position of the*/
- /* last element in FIRST_ELEMENT that was allocated. */
- /* NOTE: The suffix indentified by ROOT and TAIL is presumed not to be empty.*/
- /* That is, ROOT <= TAIL !!! */
- /*****************************************************************************/
- static short first_map(int root, int tail)
- {
- int i,
- j,
- k;
-
- for (i = first_table[rhs_sym[root]]; i != NIL; i = first_element[i].link)
- {
- for (j = root + 1,
- k = first_element[i].suffix_root + 1;
- (j <= tail && k <= first_element[i].suffix_tail);
- j++, k++)
- {
- if (rhs_sym[j] != rhs_sym[k])
- break;
- }
- if (j > tail && k > first_element[i].suffix_tail)
- return((short) i);
- }
- top++;
- first_element[top].suffix_root = root;
- first_element[top].suffix_tail = tail;
- first_element[top].link = first_table[rhs_sym[root]];
- first_table[rhs_sym[root]] = top;
-
- return(top);
- }
-
-
- /*****************************************************************************/
- /* S_FIRST: */
- /*****************************************************************************/
- /* S_FIRST takes as argument, two pointers: ROOT and TAIL to a sequence of */
- /* symbols in the vector RHS, and INDEX which is the index of a first set. */
- /* It computes the set of all terminals that can appear as the first symbol */
- /* in the sequence and places the result in the FIRST set indexable by INDEX.*/
- /*****************************************************************************/
- static void s_first(int root, int tail, int index)
- {
- int i,
- symbol;
-
- symbol = (root > tail ? empty : rhs_sym[root]);
-
- if (symbol IS_A_TERMINAL)
- {
- INIT_FIRST(index);
- SET_BIT_IN(first, index, symbol); /* add it to set */
- }
- else
- {
- ASSIGN_SET(first, index, nt_first, symbol);
- }
-
- for (i = root + 1; i <= tail && IS_IN_SET(first, index, empty); i++)
- {
- symbol = rhs_sym[i];
- RESET_BIT_IN(first, index, empty); /* remove EMPTY */
- if (symbol IS_A_TERMINAL)
- {
- SET_BIT_IN(first, index, symbol); /* add it to set */
- }
- else
- {
- SET_UNION(first, index, nt_first, symbol);
- }
- }
-
- return;
- }
-
-
- /******************************************************************/
- /* COMPUTE_PRODUCES: */
- /******************************************************************/
- /* For a given symbol, complete the computation of */
- /* PRODUCES[symbol]. */
- /******************************************************************/
- static void compute_produces(int symbol)
- {
- int indx;
-
- int new_symbol;
-
- struct node *p,
- *q;
-
- stack[++top] = symbol;
- indx = top;
- index_of[symbol] = indx;
-
- for (p = direct_produces[symbol]; p != NULL; q = p, p = p -> next)
- {
- new_symbol = p -> value;
- if (index_of[new_symbol] == OMEGA) /* first time seen? */
- compute_produces(new_symbol);
- index_of[symbol] = MIN(index_of[symbol], index_of[new_symbol]);
- NTSET_UNION(produces, symbol, produces, new_symbol);
- }
- if (direct_produces[symbol] != NULL)
- free_nodes(direct_produces[symbol], q);
-
- if (index_of[symbol] == indx) /*symbol is SCC root */
- {
- for (new_symbol = stack[top];
- new_symbol != symbol; new_symbol = stack[--top])
- {
- ASSIGN_NTSET(produces, new_symbol, produces, symbol);
- index_of[new_symbol] = INFINITY;
- }
-
- index_of[symbol] = INFINITY;
- top--;
- }
-
- return;
- }
-
-
- /*****************************************************************************/
- /* COMPUTE_FOLLOW: */
- /*****************************************************************************/
- /* COMPUTE_FOLLOW computes FOLLOW[nt] for some non-terminal NT using the */
- /* digraph algorithm. FOLLOW[NT] is the set of all terminals Ti that */
- /* may immediately follow a string X generated by NT. I.e., if NT *::= X */
- /* then X Ti is a valid substring of a class of strings that may be */
- /* recognized by the language. */
- /*****************************************************************************/
- static void compute_follow(int nt)
- {
- int indx;
-
- int rule_no,
- lhs_symbol,
- item_no;
-
- SET_PTR temp_set;
-
- temp_set = (SET_PTR)
- calloc(1, term_set_size * sizeof(BOOLEAN_CELL));
- if (temp_set == NULL)
- nospace(__FILE__, __LINE__);
-
- /**************************************************************/
- /* FOLLOW[NT] was initialized to 0 for all non-terminals. */
- /**************************************************************/
-
- stack[++top] = nt;
- indx = top;
- index_of[nt] = indx;
-
- for (item_no = nt_items[nt]; item_no != NIL; item_no = next_item[item_no])
- { /* iterate over all items of NT */
- ASSIGN_SET(temp_set, 0, first, item_table[item_no].suffix_index);
- if (IS_ELEMENT(temp_set, empty))
- {
- RESET_BIT(temp_set, empty);
- rule_no = item_table[item_no].rule_number;
- lhs_symbol = rules[rule_no].lhs;
- if (index_of[lhs_symbol] == OMEGA)
- compute_follow(lhs_symbol);
- SET_UNION( follow, nt, follow, lhs_symbol);
- index_of[nt] = MIN( index_of[nt], index_of[lhs_symbol]);
- }
- SET_UNION(follow, nt, temp_set, 0);
- }
-
- if (index_of[nt] == indx)
- {
- for (lhs_symbol = stack[top];
- lhs_symbol != nt; lhs_symbol = stack[--top])
- {
- ASSIGN_SET(follow, lhs_symbol, follow, nt);
- index_of[lhs_symbol] = INFINITY;
- }
- index_of[nt] = INFINITY;
- top--;
- }
- ffree(temp_set);
- return;
- }
-
-
- /*****************************************************************************/
- /* PRINT_UNREACHABLES: */
- /*****************************************************************************/
- static void print_unreachables(void)
- {
- short *symbol_list;
-
- int nt,
- t_root,
- nt_root,
- rule_no,
- symbol,
- i;
-
- BOOLEAN end_node;
-
- char line[PRINT_LINE_SIZE + 1],
- tok[SYMBOL_SIZE + 1];
-
- /***************************************************************/
- /* SYMBOL_LIST is used for two purposes: */
- /* 1) to mark symbols that are reachable from the Accepting */
- /* non-terminal. */
- /* 2) to construct lists of symbols that are not reachable. */
- /***************************************************************/
-
- symbol_list = Allocate_short_array(num_symbols + 1);
- for ALL_SYMBOLS(i)
- symbol_list[i] = OMEGA;
- symbol_list[eoft_image] = NIL;
- symbol_list[empty] = NIL;
- if (error_maps_bit)
- symbol_list[error_image] = NIL;
-
- /*********************************************************************/
- /* Initialize a list consisting only of the Accept non-terminal. */
- /* This list is a work pile of non-terminals to process as follows: */
- /* Each non-terminal in the front of the list is removed in turn and */
- /* 1) All terminal symbols in one of its right-hand sides are */
- /* marked reachable. */
- /* 2) All non-terminals in one of its right-hand sides are placed */
- /* in the the work pile of it had not been processed previously */
- /*********************************************************************/
- nt_root = accept_image;
- symbol_list[nt_root] = NIL;
-
- for (nt = nt_root; nt != NIL; nt = nt_root)
- {
- nt_root = symbol_list[nt];
-
- for (end_node = ((rule_no = lhs_rule[nt]) == NIL);
- ! end_node;
- end_node = (rule_no == lhs_rule[nt]))
- {
- rule_no = next_rule[rule_no];
- for ENTIRE_RHS(i, rule_no)
- {
- symbol = rhs_sym[i];
- if (symbol IS_A_TERMINAL)
- symbol_list[symbol] = NIL;
- else if (symbol_list[symbol] == OMEGA)
- {
- symbol_list[symbol] = nt_root;
- nt_root = symbol;
- }
- }
- }
- }
-
- /***************************************************************/
- /* We now iterate (backwards to keep things in order) over the */
- /* terminal symbols, and place each unreachable terminal in a */
- /* list. If the list is not empty, we signal that these symbols*/
- /* are unused. */
- /***************************************************************/
- t_root = NIL;
- for ALL_TERMINALS_BACKWARDS(symbol)
- {
- if (symbol_list[symbol] == OMEGA)
- {
- symbol_list[symbol] = t_root;
- t_root = symbol;
- }
- }
-
- if (t_root != NIL)
- {
- PR_HEADING;
- if (symbol_list[t_root] != NIL)
- {
- PRNT("*** The following Terminals are useless: ");
- fprintf(syslis, "\n\n");
- strcpy(line, " "); /* 8 spaces */
- }
- else
- strcpy(line, "*** The following Terminal is useless: ");
-
- for (symbol = t_root; symbol != NIL; symbol = symbol_list[symbol])
- {
- restore_symbol(tok, RETRIEVE_STRING(symbol));
- if (strlen(line) + strlen(tok) > PRINT_LINE_SIZE)
- {
- PRNT(line);
- print_large_token(line, tok, " ", LEN);
- }
- else
- {
- strcat(line, tok);
- strcat(line, BLANK);
- }
- strcat(line, BLANK);
- }
- PRNT(line);
- }
-
-
- /***************************************************************/
- /* We now iterate (backward to keep things in order) over the */
- /* non-terminals, and place each unreachable non-terminal in a */
- /* list. If the list is not empty, we signal that these */
- /* symbols are unused. */
- /***************************************************************/
- nt_root = NIL;
- for ALL_NON_TERMINALS_BACKWARDS(symbol)
- {
- if (symbol_list[symbol] == OMEGA)
- {
- symbol_list[symbol] = nt_root;
- nt_root = symbol;
- }
- }
-
- if (nt_root != NIL)
- {
- PR_HEADING;
- if (symbol_list[nt_root] != NIL)
- {
- PRNT("*** The following Non-Terminals are useless: ");
- fprintf(syslis, "\n\n");
- strcpy(line, " "); /* 8 spaces */
- }
- else
- strcpy(line, "*** The following Non-Terminal is useless: ");
-
- for (symbol = nt_root; symbol != NIL; symbol = symbol_list[symbol])
- {
- restore_symbol(tok, RETRIEVE_STRING(symbol));
- if (strlen(line) + strlen(tok) > PRINT_LINE_SIZE)
- {
- PRNT(line);
- print_large_token(line, tok, " ", LEN);
- }
- else
- strcat(line, tok);
- strcat(line, BLANK);
- }
- PRNT(line);
- }
-
- ffree(symbol_list);
-
- return;
- }
-
-
- /*****************************************************************************/
- /* PRINT_XREF: */
- /*****************************************************************************/
- /* PRINT_XREF prints out the Cross-reference map. We build a map from each */
- /* terminal into the set of items whose Dot-symbol (symbol immediately */
- /* following the dot ) is the terminal in question. Note that we iterate */
- /* backwards over the rules to keep the rules associated with the items */
- /* sorted, since they are inserted in STACK fashion in the lists: Last-in, */
- /* First out. */
- /*****************************************************************************/
- static void print_xref(void)
- {
- short *sort_sym,
- *t_items;
-
- int i,
- offset,
- rule_no,
- item_no,
- symbol;
-
- char line[PRINT_LINE_SIZE + 1],
- tok[SYMBOL_SIZE + 1];
-
- /*********************************************************************/
- /* SORT_SYM is used to sort the symbols for cross_reference listing. */
- /*********************************************************************/
- sort_sym = Allocate_short_array(num_symbols + 1);
- t_items = Allocate_short_array(num_terminals + 1);
-
- for ALL_TERMINALS(i)
- t_items[i] = NIL;
-
- for ALL_RULES_BACKWARDS(rule_no)
- {
- item_no = first_item_of[rule_no];
- for ENTIRE_RHS(i, rule_no)
- {
- symbol = rhs_sym[i];
- if (symbol IS_A_TERMINAL)
- {
- next_item[item_no] = t_items[symbol];
- t_items[symbol] = item_no;
- }
- item_no++;
- }
- }
- for ALL_SYMBOLS(i)
- sort_sym[i] = i;
- quick_sym(sort_sym, 1, num_symbols);
-
- PR_HEADING;
- fprintf(syslis, "\n\nCross-reference table:\n");
- output_line_no += 3;
- for ALL_SYMBOLS(i)
- {
- symbol = sort_sym[i];
- if (symbol != accept_image && symbol != eoft_image
- && symbol != empty)
- {
- fprintf(syslis, "\n");
- ENDPAGE_CHECK;
- restore_symbol(tok, RETRIEVE_STRING(symbol));
- print_large_token(line, tok, "", PRINT_LINE_SIZE-7);
- strcat(line, " ==>> ");
- offset = strlen(line) - 1;
- if (symbol IS_A_NON_TERMINAL)
- {
- BOOLEAN end_node;
-
- for (end_node = ((rule_no = lhs_rule[symbol]) == NIL);
- ! end_node;
- end_node = (rule_no == lhs_rule[symbol]))
- {
- rule_no = next_rule[rule_no];
- sprintf(tok, "%d", rule_no);
- if (strlen(tok) + strlen(line) > PRINT_LINE_SIZE)
- {
- int j;
-
- fprintf(syslis, "\n%s", line);
- ENDPAGE_CHECK;
- strcpy(line, BLANK);
- for (j = 1; j <= offset; j++)
- strcat(line, BLANK);
- }
- strcat(line, tok);
- strcat(line, BLANK);
- }
- fprintf(syslis, "\n%s", line);
- ENDPAGE_CHECK;
- item_no = nt_items[symbol];
- }
- else
- {
- for (item_no = t_items[symbol];
- item_no != NIL; item_no = next_item[item_no])
- {
- rule_no = item_table[item_no].rule_number;
- sprintf(tok, "%d", rule_no);
- if (strlen(tok) + strlen(line) > PRINT_LINE_SIZE)
- {
- int j;
-
- fprintf(syslis, "\n%s", line);
- ENDPAGE_CHECK;
- strcpy(line, BLANK);
- for (j = 1; j <= offset; j++)
- strcat(line, BLANK);
- }
- strcat(line, tok);
- strcat(line, BLANK);
- }
- fprintf(syslis, "\n%s",line);
- ENDPAGE_CHECK;
- }
- }
- }
- fprintf(syslis, "\n\n");
- output_line_no +=2;
- ffree(t_items);
- ffree(sort_sym);
-
- return;
- }
-
-
- /*****************************************************************************/
- /* QUICK_SYM: */
- /*****************************************************************************/
- /* QUICK_SYM takes as arguments an array of pointers whose elements point to */
- /* nodes and two integer arguments: L, H. L and H indicate respectively the */
- /* lower and upper bound of a section in the array. */
- /*****************************************************************************/
- static void quick_sym(short array[], int l, int h)
- {
- /**************************************************************/
- /* Since no more than 2**15-1 symbols are allowed, the stack */
- /* not grow past 14. */
- /**************************************************************/
-
- int lostack[14],
- histack[14],
- lower,
- upper,
- top,
- i,
- j;
-
- short temp,
- pivot;
-
- top = 1;
- lostack[top] = l;
- histack[top] = h;
-
- while(top != 0)
- {
- lower = lostack[top];
- upper = histack[top--];
-
- while(upper > lower)
- {
- /********************************************************/
- /* Split the array section indicated by LOWER and UPPER */
- /* using ARRAY[LOWER] as the pivot. */
- /********************************************************/
- i = lower;
- pivot = array[lower];
- for (j = lower + 1; j <= upper; j++)
- {
- if (strcmp(RETRIEVE_STRING(array[j]),
- RETRIEVE_STRING(pivot)) < 0)
- {
- temp = array[++i];
- array[i] = array[j];
- array[j] = temp;
- }
- }
-
- array[lower] = array[i];
- array[i] = pivot;
-
- top++;
- if ((i - lower) < (upper - i))
- {
- lostack[top] = i + 1;
- histack[top] = upper;
- upper = i - 1;
- }
- else
- {
- histack[top] = i - 1;
- lostack[top] = lower;
- lower = i + 1;
- }
- }
- }
-
- return;
- }
-
-
- /*****************************************************************************/
- /* PRINT_NT_FIRST: */
- /*****************************************************************************/
- /* PRINT_NT_FIRST prints the first set for each non-terminal. */
- /*****************************************************************************/
- static void print_nt_first(void)
- {
- int nt,
- t;
-
- char line[PRINT_LINE_SIZE + 1],
- tok[SYMBOL_SIZE + 1];
-
- PR_HEADING;
- fprintf(syslis, "\nFirst map for non-terminals:\n\n");
- output_line_no += 3;
-
- for ALL_NON_TERMINALS(nt)
- {
- restore_symbol(tok, RETRIEVE_STRING(nt));
- print_large_token(line, tok, "", PRINT_LINE_SIZE - 7);
- strcat(line, " ==>> ");
- for ALL_TERMINALS(t)
- {
- if (IS_IN_SET(nt_first, nt, t))
- {
- restore_symbol(tok, RETRIEVE_STRING(t));
- if (strlen(line) + strlen(tok) > PRINT_LINE_SIZE - 1)
- {
- fprintf(syslis, "\n%s", line);
- ENDPAGE_CHECK;
- print_large_token(line, tok, " ", LEN);
- }
- else
- strcat(line, tok);
- strcat(line, BLANK);
- }
- }
- fprintf(syslis, "\n%s\n", line);
- output_line_no++;
- ENDPAGE_CHECK;
- }
-
- return;
- }
-
-
- /*****************************************************************************/
- /* PRINT_FOLLOW_MAP: */
- /*****************************************************************************/
- /* PRINT_FOLLOW_MAP prints the follow map. */
- /*****************************************************************************/
- static void print_follow_map(void)
- {
- int nt,
- t;
-
- char line[PRINT_LINE_SIZE + 1],
- tok[SYMBOL_SIZE + 1];
-
- PR_HEADING;
- fprintf(syslis, "\nFollow Map:\n\n");
- output_line_no += 3;
-
- for ALL_NON_TERMINALS(nt)
- {
- restore_symbol(tok, RETRIEVE_STRING(nt));
- print_large_token(line, tok, "", PRINT_LINE_SIZE-7);
- strcat(line, " ==>> ");
- for ALL_TERMINALS(t)
- {
- if (IS_IN_SET(follow, nt, t))
- {
- restore_symbol(tok, RETRIEVE_STRING(t));
- if (strlen(line) + strlen(tok) > PRINT_LINE_SIZE-2)
- {
- fprintf(syslis, "\n%s", line);
- ENDPAGE_CHECK;
- print_large_token(line, tok, " ", LEN);
- }
- else
- strcat(line, tok);
- strcat(line, BLANK);
- }
- }
- fprintf(syslis, "\n%s\n", line);
- output_line_no++;
- ENDPAGE_CHECK;
- }
- return;
- }
-