/* $Id: mkpar.c,v 1.19 2024/12/14 14:36:50 tom Exp $ */

#include "defs.h"

#define NotSuppressed(p)	((p)->suppressed == 0)

#if defined(YYBTYACC)
#define MaySuppress(p)		((backtrack ? ((p)->suppressed <= 1) : (p)->suppressed == 0))
    /* suppress the preferred action => enable backtracking */
#define StartBacktrack(p)	if (backtrack && (p) != NULL && NotSuppressed(p)) (p)->suppressed = 1
#else
#define MaySuppress(p)		((p)->suppressed == 0)
#define StartBacktrack(p)	/*nothing */
#endif

static action *add_reduce(action *actions, int ruleno, int symbol);
static action *add_reductions(int stateno, action *actions);
static action *get_shifts(int stateno);
static action *parse_actions(int stateno);
static int sole_reduction(int stateno);
static void defreds(void);
static void find_final_state(void);
static void free_action_row(action *p);
static void remove_conflicts(void);
static void total_conflicts(void);
static void unused_rules(void);

action **parser;

int SRexpect;
int RRexpect;

int SRtotal;
int RRtotal;

Value_t *SRconflicts;
Value_t *RRconflicts;
Value_t *defred;
Value_t *rules_used;
Value_t nunused;
Value_t final_state;

static Value_t SRcount;
static Value_t RRcount;

void
make_parser(void)
{
    int i;

    parser = NEW2(nstates, action *);
    for (i = 0; i < nstates; i++)
	parser[i] = parse_actions(i);

    find_final_state();
    remove_conflicts();
    unused_rules();
    if (SRtotal + RRtotal > 0)
	total_conflicts();
    defreds();
}

static action *
parse_actions(int stateno)
{
    action *actions;

    actions = get_shifts(stateno);
    actions = add_reductions(stateno, actions);
    return (actions);
}

static action *
get_shifts(int stateno)
{
    action *actions, *temp;
    shifts *sp;
    Value_t *to_state2;

    actions = NULL;
    sp = shift_table[stateno];
    if (sp)
    {
	Value_t i;

	to_state2 = sp->shift;
	for (i = (Value_t)(sp->nshifts - 1); i >= 0; i--)
	{
	    Value_t k = to_state2[i];
	    Value_t symbol = accessing_symbol[k];

	    if (ISTOKEN(symbol))
	    {
		temp = NEW(action);
		temp->next = actions;
		temp->symbol = symbol;
		temp->number = k;
		temp->prec = symbol_prec[symbol];
		temp->action_code = SHIFT;
		temp->assoc = symbol_assoc[symbol];
		actions = temp;
	    }
	}
    }
    return (actions);
}

static action *
add_reductions(int stateno, action *actions)
{
    int i, j, m, n;
    int tokensetsize;

    tokensetsize = WORDSIZE(ntokens);
    m = lookaheads[stateno];
    n = lookaheads[stateno + 1];
    for (i = m; i < n; i++)
    {
	int ruleno = LAruleno[i];
	unsigned *rowp = LA + i * tokensetsize;

	for (j = ntokens - 1; j >= 0; j--)
	{
	    if (BIT(rowp, j))
		actions = add_reduce(actions, ruleno, j);
	}
    }
    return (actions);
}

static action *
add_reduce(action *actions,
	   int ruleno,
	   int symbol)
{
    action *temp, *prev, *next;

    prev = NULL;
    for (next = actions; next && next->symbol < symbol; next = next->next)
	prev = next;

    while (next && next->symbol == symbol && next->action_code == SHIFT)
    {
	prev = next;
	next = next->next;
    }

    while (next && next->symbol == symbol &&
	   next->action_code == REDUCE && next->number < ruleno)
    {
	prev = next;
	next = next->next;
    }

    temp = NEW(action);
    temp->next = next;
    temp->symbol = (Value_t)symbol;
    temp->number = (Value_t)ruleno;
    temp->prec = rprec[ruleno];
    temp->action_code = REDUCE;
    temp->assoc = rassoc[ruleno];

    if (prev)
	prev->next = temp;
    else
	actions = temp;

    return (actions);
}

static void
find_final_state(void)
{
    Value_t *to_state2;
    shifts *p;

    if ((p = shift_table[0]) != NULL)
    {
	int i;
	int goal = ritem[1];

	to_state2 = p->shift;
	for (i = p->nshifts - 1; i >= 0; --i)
	{
	    final_state = to_state2[i];
	    if (accessing_symbol[final_state] == goal)
		break;
	}
    }
}

static void
unused_rules(void)
{
    int i;
    action *p;

    rules_used = TMALLOC(Value_t, nrules);
    NO_SPACE(rules_used);

    for (i = 0; i < nrules; ++i)
	rules_used[i] = 0;

    for (i = 0; i < nstates; ++i)
    {
	for (p = parser[i]; p; p = p->next)
	{
	    if ((p->action_code == REDUCE) && MaySuppress(p))
		rules_used[p->number] = 1;
	}
    }

    nunused = 0;
    for (i = 3; i < nrules; ++i)
	if (!rules_used[i])
	    ++nunused;

    if (nunused)
    {
	if (nunused == 1)
	    fprintf(stderr, "%s: 1 rule never reduced\n", myname);
	else
	    fprintf(stderr, "%s: %ld rules never reduced\n", myname, (long)nunused);
    }
}

static void
remove_conflicts(void)
{
    int i;
    action *p, *pref = NULL;

    SRtotal = 0;
    RRtotal = 0;
    SRconflicts = NEW2(nstates, Value_t);
    RRconflicts = NEW2(nstates, Value_t);
    for (i = 0; i < nstates; i++)
    {
	int symbol = -1;

	SRcount = 0;
	RRcount = 0;
#if defined(YYBTYACC)
	pref = NULL;
#endif
	for (p = parser[i]; p; p = p->next)
	{
	    if (p->symbol != symbol)
	    {
		/* the first parse action for each symbol is the preferred action */
		pref = p;
		symbol = p->symbol;
	    }
	    /* following conditions handle multiple, i.e., conflicting, parse actions */
	    else if (i == final_state && symbol == 0)
	    {
		SRcount++;
		p->suppressed = 1;
		StartBacktrack(pref);
	    }
	    else if (pref != NULL && pref->action_code == SHIFT)
	    {
		if (pref->prec > 0 && p->prec > 0)
		{
		    if (pref->prec < p->prec)
		    {
			pref->suppressed = 2;
			pref = p;
		    }
		    else if (pref->prec > p->prec)
		    {
			p->suppressed = 2;
		    }
		    else if (pref->assoc == LEFT)
		    {
			pref->suppressed = 2;
			pref = p;
		    }
		    else if (pref->assoc == RIGHT)
		    {
			p->suppressed = 2;
		    }
		    else
		    {
			pref->suppressed = 2;
			p->suppressed = 2;
		    }
		}
		else
		{
		    SRcount++;
		    p->suppressed = 1;
		    StartBacktrack(pref);
		}
	    }
	    else
	    {
		RRcount++;
		p->suppressed = 1;
		StartBacktrack(pref);
	    }
	}
	SRtotal += SRcount;
	RRtotal += RRcount;
	SRconflicts[i] = SRcount;
	RRconflicts[i] = RRcount;
    }
}

static void
total_conflicts(void)
{
    fprintf(stderr, "%s: ", myname);
    if (SRtotal == 1)
	fprintf(stderr, "1 shift/reduce conflict");
    else if (SRtotal > 1)
	fprintf(stderr, "%d shift/reduce conflicts", SRtotal);

    if (SRtotal && RRtotal)
	fprintf(stderr, ", ");

    if (RRtotal == 1)
	fprintf(stderr, "1 reduce/reduce conflict");
    else if (RRtotal > 1)
	fprintf(stderr, "%d reduce/reduce conflicts", RRtotal);

    fprintf(stderr, ".\n");

    if (SRexpect >= 0 && SRtotal != SRexpect)
    {
	fprintf(stderr, "%s: ", myname);
	fprintf(stderr, "expected %d shift/reduce conflict%s.\n",
		SRexpect, PLURAL(SRexpect));
	exit_code = EXIT_FAILURE;
    }
    if (RRexpect >= 0 && RRtotal != RRexpect)
    {
	fprintf(stderr, "%s: ", myname);
	fprintf(stderr, "expected %d reduce/reduce conflict%s.\n",
		RRexpect, PLURAL(RRexpect));
	exit_code = EXIT_FAILURE;
    }
}

static int
sole_reduction(int stateno)
{
    int count, ruleno;
    action *p;

    count = 0;
    ruleno = 0;
    for (p = parser[stateno]; p; p = p->next)
    {
	if (p->action_code == SHIFT && MaySuppress(p))
	    return (0);
	else if ((p->action_code == REDUCE) && MaySuppress(p))
	{
	    if (ruleno > 0 && p->number != ruleno)
		return (0);
	    if (p->symbol != 1)
		++count;
	    ruleno = p->number;
	}
    }

    if (count == 0)
	return (0);
    return (ruleno);
}

static void
defreds(void)
{
    int i;

    defred = NEW2(nstates, Value_t);
    for (i = 0; i < nstates; i++)
	defred[i] = (Value_t)sole_reduction(i);
}

static void
free_action_row(action *p)
{
    action *q;

    while (p)
    {
	q = p->next;
	FREE(p);
	p = q;
    }
}

void
free_parser(void)
{
    int i;

    for (i = 0; i < nstates; i++)
	free_action_row(parser[i]);

    FREE(parser);
}

#ifdef NO_LEAKS
void
mkpar_leaks(void)
{
    DO_FREE(defred);
    DO_FREE(rules_used);
    DO_FREE(SRconflicts);
    DO_FREE(RRconflicts);
}
#endif
