/* $Id: mkstates.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 "common.h"
#include "header.h"

static void mklr0(void);
static struct state_element *lr0_state_map(struct node *kernel);

/****************************************************************************/
/* STATE_ELEMENT is used to represent states. Each state is mapped into a   */
/* unique number. The components QUEUE and LINK are auxiliary:              */
/*   QUEUE is used to form a sequential linked-list of the states ordered   */
/* STATE_NUMBER and identified by the variable STATE_ROOT.                  */
/*   LINK is used to resolve collisions in hashing the states.              */
/* NEXT_SHIFT is used to resolve collisions in hashing SHIFT maps.          */
/****************************************************************************/
struct state_element
{
    struct state_element     *link,
                             *queue,
                             *next_shift;
    struct node              *kernel_items,
                             *complete_items;
    struct shift_header_type lr0_shift;
    struct goto_header_type  lr0_goto;
    short                    shift_number,
                             state_number;
};

static struct state_element **state_table,
                            **shift_table,
                             *state_root,
                             *state_tail;

static short *shift_action;

static struct goto_header_type  no_gotos_ptr;
static struct shift_header_type no_shifts_ptr;

/*****************************************************************************/
/*                              MKSTATS:                                     */
/*****************************************************************************/
/* In this procedure, we first construct the LR(0) automaton.                */
/*****************************************************************************/
void mkstats(void)
{
    int j;

    no_gotos_ptr.size = 0;     /* For states with no GOTOs */
    no_gotos_ptr.map  = NULL;

    no_shifts_ptr.size = 0;    /* For states with no SHIFTs */
    no_shifts_ptr.map  = NULL;

    mklr0();

    if (error_maps_bit &&
        (table_opt == OPTIMIZE_TIME || table_opt == OPTIMIZE_SPACE))
        produce();

    /**********************************************************************/
    /* Free space trapped by the CLOSURE and CLITEMS maps.                */
    /**********************************************************************/
    for ALL_NON_TERMINALS(j)
    {
        struct node *p, *q;

        q = clitems[j];
        if (q != NULL)
        {
            p = q -> next;
            free_nodes(p, q);
        }

        q = closure[j];
        if (q != NULL)
        {
            p = q -> next;
            free_nodes(p, q);
        }
    }

    closure += (num_terminals + 1);
    ffree(closure);
    clitems += (num_terminals + 1);
    ffree(clitems);

    return;
}


/*****************************************************************************/
/*                               MKLR0:                                      */
/*****************************************************************************/
/* This procedure constructs an LR(0) automaton.                             */
/*****************************************************************************/
static void mklr0(void)
{
    /*****************************************************************/
    /* STATE_TABLE is the array used to hash the states. States are  */
    /* identified by their Kernel set of items. Hash locations are   */
    /* computed for the states. As states are inserted in the table, */
    /* they are threaded together via the QUEUE component of         */
    /* STATE_ELEMENT. The variable STATE_ROOT points to the root of  */
    /* the thread, and the variable STATE_TAIL points to the tail.   */
    /*                                                               */
    /*   After constructing a state, Shift and Goto actions are      */
    /* defined on that state based on a partition of the set of items*/
    /* in that state. The partitioning is based on the symbols       */
    /* following the dot in the items. The array PARTITION is used   */
    /* for that partitioning. LIST and ROOT are used to construct    */
    /* temporary lists of symbols in a state on which Shift or Goto  */
    /* actions are defined.                                          */
    /*   NT_LIST and NT_ROOT are used to build temporary lists of    */
    /* non-terminals.                                                */
    /*****************************************************************/

    struct node *p,
                *q,
                *r,
                *new_item,
                *tail,
                *item_ptr,
                **partition,
                *closure_root,
                *closure_tail;

    struct state_element *state,
                         *new_state;

    BOOLEAN end_node;

    int goto_size,
        shift_size,
        i,
        state_no,
        next_item_no,
        item_no,
        symbol,
        rule_no,
        shift_root,
        nt_root,
        root;

    struct goto_header_type go_to;

    short *shift_list,
          *nt_list,
          *list;

    /******************************************************************/
    /* Set up a a pool of temporary space.                            */
    /******************************************************************/
    reset_temporary_space();

    list = Allocate_short_array(num_symbols + 1);
    shift_action = Allocate_short_array(num_terminals + 1);
    shift_list = Allocate_short_array(num_terminals + 1);
    nt_list = Allocate_short_array(num_non_terminals);
    nt_list -= (num_terminals + 1);
    partition = (struct node **)
                calloc(num_symbols + 1, sizeof(struct node *));
    if (partition == NULL)
        nospace(__FILE__, __LINE__);
    state_table = (struct state_element **)
                  calloc(STATE_TABLE_SIZE, sizeof(struct state_element *));
    if (state_table == NULL)
        nospace(__FILE__, __LINE__);
    shift_table = (struct state_element **)
                  calloc(SHIFT_TABLE_SIZE, sizeof(struct state_element *));
    if (shift_table == NULL)
        nospace(__FILE__, __LINE__);

/* INITIALIZATION -----------------------------------------------------------*/
    goto_size = 0;
    shift_size = 0;

    state_root = NULL;

    for (i = 0; i <= num_terminals; i++)
        shift_action[i] = OMEGA;

    nt_root = NIL;
    for ALL_NON_TERMINALS(i)
        nt_list[i] = OMEGA;

   /* PARTITION, STATE_TABLE and SHIFT_TABLE are initialized by calloc */

/* END OF INITIALIZATION ----------------------------------------------------*/

    /*****************************************************************/
    /* Kernel of the first state consists of the first items in each */
    /* rule produced by Accept non-terminal.                         */
    /*****************************************************************/
    q = NULL;
    for (end_node = ((p = clitems[accept_image]) == NULL);
         ! end_node; /* Loop over circular list */
         end_node = (p == clitems[accept_image]))
    {
        p = p -> next;

        new_item = Allocate_node();
        new_item -> value = p -> value;
        new_item -> next = q;
        q = new_item;
    }

    /*****************************************************************/
    /* Insert first state in STATE_TABLE and keep constructing states*/
    /* until we no longer can.                                       */
    /*****************************************************************/

    for (state = lr0_state_map(q); /* insert initial state */
         state != NULL; /* and process next state until no more */
         state = state -> queue)
    {
        /******************************************************************/
        /* Now we construct a list of all non-terminals that can be       */
        /* introduced in this state through closure.  The CLOSURE of each */
        /* non-terminal has been previously computed in MKFIRST.          */
        /******************************************************************/
        for (q = state -> kernel_items;
             q != NULL; /* iterate over kernel set of items */
             q = q -> next)
        {
            item_no = q -> value;
            symbol = item_table[item_no].symbol;  /* symbol after dot */
            if (symbol IS_A_NON_TERMINAL)     /* Dot symbol */
            {
                if (nt_list[symbol] == OMEGA) /* not yet seen */
                {
                    nt_list[symbol] = nt_root;
                    nt_root = symbol;

                    for (end_node = ((p = closure[symbol]) == NULL);
                         ! end_node; /* Loop over circular list */
                         end_node = (p == closure[symbol]))
                    {   /* add its closure to list */
                        p = p -> next;

                        if (nt_list[p -> value] == OMEGA)
                        {
                            nt_list[p -> value] = nt_root;
                            nt_root = p -> value;
                        }
                    }
                }
            }
        }

        /*******************************************************************/
        /*   We now construct lists of all start items that the closure    */
        /* non-terminals produce.  A map from each non-terminal to its set */
        /* start items has previously been computed in MKFIRST. (CLITEMS)  */
        /* Empty items are placed directly in the state, whereas non_empty */
        /* items are placed in a temporary list rooted at CLOSURE_ROOT.    */
        /*******************************************************************/
        closure_root = NULL; /* used to construct list of closure items */
        for (symbol = nt_root; symbol != NIL;
             nt_list[symbol] = OMEGA, symbol = nt_root)
        {
            nt_root = nt_list[symbol];

            for (end_node = ((p = clitems[symbol]) == NULL);
                 ! end_node;  /* Loop over circular list */
                 end_node = (p == clitems[symbol]))
            {
                p = p -> next;

                item_no = p -> value;
                new_item = Allocate_node();
                new_item -> value = item_no;

                if (item_table[item_no].symbol == empty) /* complete item */
                {
                    /* Add to COMPLETE_ITEMS set in state */
                    new_item -> next = state -> complete_items;
                    state -> complete_items = new_item;
                }
                else
                {   /* closure item, add to closure list */
                    if (closure_root == NULL)
                        closure_root = new_item;
                    else
                        closure_tail -> next = new_item;
                    closure_tail = new_item;
                }
            }
        }

        if  (closure_root != NULL) /* any non-complete closure items? */
        {
            /* construct list of them and kernel items */
            closure_tail -> next = state -> kernel_items;
            item_ptr = closure_root;
        }
        else  /* else just consider kernel items */
            item_ptr = state -> kernel_items;

        /*******************************************************************/
        /*   In this loop, the PARTITION map is constructed. At this point,*/
        /* ITEM_PTR points to all the non_complete items in the closure of */
        /* the state, plus all the kernel items.  We note that the kernel  */
        /* items may still contain complete-items, and if any is found, the*/
        /* COMPLETE_ITEMS list is updated.                                 */
        /*******************************************************************/
        root = NIL;
        for (; item_ptr != NULL; item_ptr = item_ptr -> next)
        {
            item_no = item_ptr -> value;
            symbol = item_table[item_no].symbol;
            if (symbol != empty)  /* incomplete item */
            {
                next_item_no = item_no + 1;
                
                if (partition[symbol] == NULL)
                {   /* PARTITION not defined on symbol */
                    list[symbol] = root;  /* add to list */
                    root = symbol;
                    if (symbol IS_A_TERMINAL) /* Update transition count */
                        shift_size++;
                    else
                        goto_size++;
                }
                for (p = partition[symbol];
                     p != NULL;
                     tail = p, p = p -> next)
                {
                    if (p -> value > next_item_no)
                        break;
                }

                r = Allocate_node();
                r -> value = next_item_no;
                r -> next = p;

                if (p == partition[symbol]) /* Insert at beginning */
                    partition[symbol] = r;
                else
                    tail -> next = r;
            }
            else /* Update complete item set with item from kernel */
            {
                 p = Allocate_node();
                 p -> value = item_no;
                 p -> next = state -> complete_items;
                 state -> complete_items = p;
            }
        }
        if (closure_root != NULL)
            free_nodes(closure_root, closure_tail);

        /*******************************************************************/
        /* We now iterate over the set of partitions and update the state  */
        /* automaton and the transition maps: SHIFT and GOTO. Each         */
        /* partition represents the kernel of a state.                     */
        /*******************************************************************/
        if (goto_size > 0)
        {
            go_to = Allocate_goto_map(goto_size);
            state -> lr0_goto = go_to;
        }
        else
            state -> lr0_goto = no_gotos_ptr;

        shift_root = NIL;
        for (symbol = root;
             symbol != NIL; /* symbols on which transition is defined */
             symbol = list[symbol])
        {
            short action = OMEGA;

            /*****************************************************************/
            /* If the partition contains only one item, and it is adequate   */
            /* (i.e. the dot immediately follows the last symbol), and       */
            /* READ-REDUCE is requested, a new state is not created, and the */
            /* action is marked as a Shift-reduce or a Goto-reduce. Otherwise*/
            /* if a state with that kernel set does not yet exist, we create */
            /* it.                                                           */
            /*****************************************************************/
            q = partition[symbol]; /* kernel of a new state */
            if (read_reduce_bit && q -> next == NULL)
            {
                item_no = q -> value;
                if (item_table[item_no].symbol == empty)
                {
                    rule_no = item_table[item_no].rule_number;
                    if (rules[rule_no].lhs != accept_image)
                    {
                        action = -rule_no;
                        free_nodes(q, q);
                    }
                }
            }

            if (action == OMEGA) /* Not a Read-Reduce action */
            {
                new_state = lr0_state_map(q);
                action = new_state -> state_number;
            }

            /****************************************************************/
            /* At this stage, the partition list has been freed (for an old */
            /* state or an ADEQUATE item), or used (for a new state).  The  */
            /* PARTITION field involved should be reset.                    */
            /****************************************************************/
            partition[symbol] = NULL;           /* To be reused */

            /*****************************************************************/
            /* At this point, ACTION contains the value of the state to Shift*/
            /* to, or rule to Read-Reduce on. If the symbol involved is a    */
            /* terminal, we update the Shift map; else, it is a non-terminal */
            /* and we update the Goto map.                                   */
            /* Shift maps are constructed temporarily in SHIFT_ACTION.       */
            /* Later, they are inserted into a map of unique Shift maps, and */
            /* shared by states that contain identical shifts.               */
            /* Since the lookahead set computation is based on the GOTO maps,*/
            /* all these maps and their element maps should be kept as       */
            /* separate entities.                                            */
            /*****************************************************************/
            if (symbol IS_A_TERMINAL) /* terminal? add to SHIFT map */
            {
                shift_action[symbol] = action;
                shift_list[symbol] = shift_root;
                shift_root = symbol;
                if (action > 0)
                    num_shifts++;
                else
                    num_shift_reduces++;
            }

            /*****************************************************************/
            /* NOTE that for Goto's we update the field LA_PTR of GOTO. This */
            /* field will be used later in the routine MKRDCTS to point to a */
            /* look-ahead set.                                               */
            /*****************************************************************/
            else
            {
                GOTO_SYMBOL(go_to, goto_size) = symbol; /* symbol field */
                GOTO_ACTION(go_to, goto_size) = action; /* state field  */
                GOTO_LAPTR(go_to,  goto_size) = OMEGA;  /* la_ptr field */
                goto_size--;
                if (action > 0)
                    num_gotos++;
                else
                    num_goto_reduces++;
            }
        }

        /*******************************************************************/
        /* We are now going to update the set of Shift-maps. Ths idea is   */
        /* to do a look-up in a hash table based on SHIFT_TABLE to see if  */
        /* the Shift map associated with the current state has already been*/
        /* computed. If it has, we simply update the SHIFT_NUMBER and the  */
        /* SHIFT field of the current state. Otherwise, we allocate and    */
        /* construct a SHIFT_ELEMENT map, update the current state, and    */
        /* add it to the set of Shift maps in the hash table.              */
        /*   Note that the SHIFT_NUMBER field in the STATE_ELEMENTs could  */
        /* have been factored out and associated instead with the          */
        /* SHIFT_ELEMENTs. That would have saved some space, but only in   */
        /* the short run. This field was purposely stored in the           */
        /* STATE_ELEMENTs, because once the states have been constructed,  */
        /* they are not kept, whereas the SHIFT_ELEMENTs are kept.         */
        /*    One could have also threaded through the states that contain */
        /* original shift maps so as to avoid duplicate assignments in     */
        /* creating the SHIFT map later. However, this would have          */
        /* increased the storage requirement, and would probably have saved*/
        /* (at most) a totally insignificant amount of time.               */
        /*******************************************************************/
update_shift_maps:
        {
            unsigned long hash_address;

            struct shift_header_type sh;

            struct state_element *p;

            hash_address = shift_size;
            for (symbol = shift_root;
                 symbol != NIL; symbol = shift_list[symbol])
            {
                hash_address += ABS(shift_action[symbol]);
            }
            hash_address %= SHIFT_TABLE_SIZE;

            for (p = shift_table[hash_address];
                 p != NULL; /* Search has table for shift map */
                 p = p -> next_shift)
            {
                sh = p -> lr0_shift;
                if (sh.size == shift_size)
                {
                    for (i = 1; i <= shift_size; i++) /* Compare shift maps */
                    {
                        if (SHIFT_ACTION(sh, i) !=
                            shift_action[SHIFT_SYMBOL(sh, i)])
                            break;
                    }

                    if (i > shift_size)  /* Are they equal ? */
                    {
                        state -> lr0_shift    = sh;
                        state -> shift_number = p -> shift_number;
                        for (symbol = shift_root;
                             symbol != NIL; symbol = shift_list[symbol])
                        { /* Clear SHIFT_ACTION */
                            shift_action[symbol] = OMEGA;
                        }
                        shift_size = 0;   /* Reset for next round */
                        goto leave_update_shift_maps;
                    }
                }
            }

            if (shift_size > 0)
            {
                sh = Allocate_shift_map(shift_size);
                num_shift_maps++;
                state -> shift_number = num_shift_maps;
            }
            else
            {
                state -> shift_number = 0;
                sh = no_shifts_ptr;
            }

            state -> lr0_shift = sh;
            state -> next_shift = shift_table[hash_address];
            shift_table[hash_address] = state;

            for (symbol = shift_root;
                 symbol != NIL; symbol = shift_list[symbol])
            {
                SHIFT_SYMBOL(sh, shift_size) = symbol;
                SHIFT_ACTION(sh, shift_size) = shift_action[symbol];
                shift_action[symbol] = OMEGA;
                shift_size--;
            }
        } /*end update_shift_maps */

leave_update_shift_maps:;
    }

    /*********************************************************************/
    /* Construct STATSET, a "compact" and final representation of        */
    /* State table, and SHIFT which is a set of all shift maps needed.   */
    /* NOTE that assignments to elements of SHIFT may occur more than    */
    /* once, but that's ok. It is probably faster to  do that than to    */
    /* set up an elaborate scheme to avoid the multiple assignment which */
    /* may in fact cost more.  Look at it this way: it is only a pointer */
    /* assignment, namely a Load and a Store.                            */
    /* Release all NODEs used by  the maps CLITEMS and CLOSURE.          */
    /*********************************************************************/
    {
        struct state_element *p;

    /*********************************************************************/
    /* If the grammar is LALR(k), k > 1, more states may be added and    */
    /* the size of the shift map increased.                              */
    /*********************************************************************/
        shift = (struct shift_header_type *)
                calloc(num_states + 1, sizeof(struct shift_header_type));
        if (shift == NULL)
            nospace(__FILE__, __LINE__);
        shift[0] = no_shifts_ptr;        /* MUST be initialized for LALR(k) */
        statset = (struct statset_type *)
                  calloc(num_states + 1, sizeof(struct statset_type));
        if (statset == NULL)
            nospace(__FILE__, __LINE__);

        for (p = state_root; p != NULL; p = p -> queue)
        {
            state_no = p -> state_number;
            statset[state_no].kernel_items = p -> kernel_items;
            statset[state_no].complete_items = p -> complete_items;
            shift[p -> shift_number] = p -> lr0_shift;
            statset[state_no].shift_number = p -> shift_number;
            statset[state_no].go_to = p -> lr0_goto;
        }
    }

    ffree(list);
    ffree(shift_action);
    ffree(shift_list);
    nt_list += (num_terminals + 1);
    ffree(nt_list);
    ffree(partition);
    ffree(state_table);
    ffree(shift_table);

    return;
}


/*****************************************************************************/
/*                            LR0_STATE_MAP:                                 */
/*****************************************************************************/
/* LR0_STATE_MAP takes as an argument a pointer to a kernel set of items. If */
/* no state based on that kernel set already exists, then a new one is       */
/* created and added to STATE_TABLE. In any case, a pointer to the STATE of  */
/* the KERNEL is returned.                                                   */
/*****************************************************************************/
static struct state_element *lr0_state_map(struct node *kernel)
{
    unsigned long hash_address = 0;
    struct node *p;
    struct state_element *state_ptr,
                         *ptr;

    /*********************************************/
    /*       Compute the hash address.           */
    /*********************************************/
    for (p = kernel; p != NULL; p = p -> next)
    {
        hash_address += p -> value;
    }
    hash_address %= STATE_TABLE_SIZE;

    /*************************************************************************/
    /* Check whether a state is already defined by the KERNEL set.           */
    /*************************************************************************/
    for (state_ptr = state_table[hash_address];
         state_ptr != NULL; state_ptr = state_ptr -> link)
    {
        struct node *q,
                    *r;

        for (p = state_ptr -> kernel_items, q = kernel;
             p != NULL && q != NULL;
             p = p -> next, r = q, q = q -> next)
        {
            if (p -> value != q -> value)
                break;
        }

        if (p == q)  /* Both P and Q are NULL? */
        {
            free_nodes(kernel, r);
            return(state_ptr);
        }
    }


    /*******************************************************************/
    /* Add a new state based on the KERNEL set.                        */
    /*******************************************************************/
    ptr = (struct state_element *) talloc(sizeof(struct state_element));
    if (ptr == (struct state_element *) NULL)
        nospace(__FILE__, __LINE__);

    num_states++;
    SHORT_CHECK(num_states);
    ptr -> queue = NULL;
    ptr -> kernel_items = kernel;
    ptr -> complete_items = NULL;
    ptr -> state_number = num_states;
    ptr -> link = state_table[hash_address];
    state_table[hash_address] = ptr;
    if (state_root == NULL)
        state_root = ptr;
    else
        state_tail -> queue = ptr;

    state_tail = ptr;

    return(ptr);
}
