/*	PG_PARSE.C
	Copyright (C) 1992	Keith L. Robertson	All Rights Reserved

	Implementation of parser.
	Need to add table produced by the PGEN Parser Generator.

    Any user of 'parse()' must ensure that the following are defined:

1)	The type  'LEX_INFO', that defines info relevant to token
	lexemes and parser nonterminal symbols.

2)	Global variable  'lexeme' of type LEX_INFO in which the
	lexer returns information related to the token it last saw.

3)	Global variable  'USHORT last_token', which is the token number
	returned by the last call to 'get_token()'.

4)	Function  'USHORT  get_token()', which reads the input stream and
	returns the token it saw, with related information in 'lexeme'.

5)	Procedure  'VOID  log_error (USHORT etype,  CSTRING desc)', which
	prints an error message based on the given error type and
	description.  See EXPR_LEX.H for a description.

6)	Two operations on type  'LEX_INFO', probably through macros:

	    VOID	set_jumped(LEX_INFO& lex_info, INT value), or
	    #define	set_jumped(lex_info,value)
		// Mark 'lex_info' as jumped if 'value' is true,
		//  and as not jumped if 'value' is false.
		// Used by the parser to mark/unmark stack objects.
	    INT		was_jumped(LEX_INFO& lex_info), or
	    #define	was_jumped(lex_info)
		// Return true if 'lex_info' is marked as jumped.

	The lexical analyzer must ensure that 'lexeme' is marked as not jumped.
	The  'LEX_INFO&' sytax above is actually valid C++, not C.
	It means that in the call, you specify the structure to pass without
	the '&' address-of operator, and the pointer is actually passed.
	Therefore, call using  "was_jumped (attr [num])", for example,
	NOT  "was_jumped (& attr [num])".
*/
/* Lexer header must go first to define LEX_INFO. */
#include "expr_lex.h"		/* Get token definitions */
	  /**** Replace with your lexical analyzer header file. ****/
#include "pg_parse.h"		/* Get type definitions for parse tables */

/* If STACK_SIZE is set (nonzero), the stack will be checked	*/
/* for overflow each time something is pushed onto it.		*/
#define		STACK_SIZE		0


extern USHORT		state_stack [];
extern LEX_INFO		state_value [];

LEX_INFO*	attr;	/* Pass attribute info to semantic actions */


/* ------------------------------------- */

/* Return true if bit 'bitidx' is set in 'bitmap' */
BOOL	bitlookup (USHORT* bitmap,  USHORT bitidx)
{   USHORT	idx = bitidx >> 4;
    USHORT	bmp = 1 << (bitidx & 0xF);
    return  bitmap [idx] & bmp;
}

VOID	bitset (USHORT* bitmap,  USHORT bitidx,  BOOL value)
{   USHORT	idx = bitidx >> 4;
    USHORT	bmp = 1 << (bitidx & 0xF);
    if  (! value)	{ bitmap [idx] &= ~bmp; }
    else		{ bitmap [idx] |=  bmp; }
}


/* ------------------------------------- */

extern ACTION_ITEM	action_list   [];
extern USHORT		action_index  [];

static ACTION _near	action (USHORT state,  USHORT next)
{   USHORT	idx = action_index [state];
    ACTION	ANY_action = no_change (0);
    ACTION	act;
    while  (1)  {
	USHORT	tok = action_list [idx].token;
	if  (tok == ANY_TK)  {
	    if  (ANY_action != no_change (0))  { act = ANY_action; }
	    else	{ act = action_list [idx].action; }
	    break;
	}
	else if  (tok == next)  {
	    act = action_list [idx].action;
	    break;
	}
	else if  (tok & BR)  {
	    if  (ANY_action == no_change (0))  {
		ANY_action = action_list [idx].action;
	    }
	    idx = (tok ^ BR);
	}
	else	{ ++idx; }
    }
    return  act;
}


/* ------------------------------------- */

extern GOTO_LIST_ITEM	goto_list [];
extern USHORT		goto_index [];

static USHORT _near	go_to (USHORT state,  USHORT nonterm)
{   USHORT	idx = goto_index [nonterm];
    while  (1)  {
	USHORT	chkst = goto_list [idx].cur_state;
	if  (chkst == ANY_ST  ||  chkst == state)  { break; }
	++idx;
    }
    return  goto_list [idx].nxt_state;
}


/* ------------------------------------- */

extern PRODUCTION_INFO	production [];

SHORT	top = 0;	/* Index of top state on stack.  Can go negative. */

VOID	parse ()
{
    PRODUCTION_INFO*	pr;
    USHORT	next_tok = get_token ();

    state_stack [top] = 0;	/* Start in state 0 */

    while  (1)  {
	ACTION	act = action (state_stack [top], next_tok);
	USHORT	sec = second_part (act);

	switch  (action_part (act))  {
	/* ------------------------------------- */
	case  JUMP_ACTION:
	    if  (act == error(0))  {
		/* Token seen was not expected. */
		do  {
		    act = action (state_stack [top], ERR_TK);
		    if  (action_part (act) == SHIFT_ACTION)	{ break; }
		    --top;
		}   while  (top >= 0);
		if  (top < 0)  {
		    log_error (FATAL, "no error processing available for syntax error.");
		}
		else if  (next_tok == 0)  {
		    log_error (FATAL, "premature end of input.");
		}

		/* Push to an error state, but don't consume token. */
		state_stack [++top] = second_part (act);
	    }
	    else  {
		/* An optional nonterminal is being jumped. */
		++ top;

		#if  STACK_SIZE
		    if  (STACK_SIZE <= top)  { goto STACK_OVERFLOW; }
		#endif

		/* 'sec' is the state being jumped. */
		state_stack [top] = sec;
		/* Mark it as a jumped lexeme. */
		set_jumped (state_value [top], 1);
	    }
	    break;

	/* ------------------------------------- */
	case  REDUCE_ACTION:
	    pr = & production [sec];

		/* Reset 'top' to point to state beginning the production. */
		/* (An empty production will push a new state on the stack.) */
	    top = top - pr->size_m1;

	    #if  STACK_SIZE
		if  (STACK_SIZE <= top)  { goto  STACK_OVERFLOW; }
	    #endif

	    if  (pr->sem_action)  {
		/* Set up global for semantic action. */
		attr = & (state_value [top]);
		(* pr->sem_action) ();	/* Do semantic action. */
	    }

	    /* Assign the go_to state after reduction */
	    state_stack [top] = go_to (state_stack [top-1], pr->nonterm);

	    if  (was_jumped (state_value [top]))  {
		/* This nonterm is not jumped, but it may have begun with */
		/* a jumped symbol.  Ensure it is marked as not jumped.   */
		set_jumped (state_value [top],  0);
	    }

	    /* Reassign in case sem action called get_token(). */
	    next_tok = last_token;
	    break;

	/* ------------------------------------- */
	case  SHIFT_ACTION:
	    ++ top;

	    #if  STACK_SIZE
		if  (STACK_SIZE <= top)  {
		STACK_OVERFLOW:
		    log_error (FATAL, "input too complex.  Parse stack overflow.");
		}
	    #endif

	    state_stack [top] = sec;		/* Push state shifted to */
	    state_value [top] = lexeme;		/* Assign token value */
	    /* The lexical analyzer must ensure that */
	    /* its tokens are marked as not jumped.  */

	    next_tok = get_token ();		/* Advance input stream */
	    break;

	/* ------------------------------------- */
	case  ACCEPT_ACTION:
	    goto  DONE;		/* Break out of loop. */

	/* ------------------------------------- */
	}
    }
    DONE:	;
}


/* ------------------------------------- */

VOID	skip_to_token (USHORT tok)
{   while  (last_token != tok  &&  last_token != EOS)  {
	get_token ();
    }
}

VOID	skip_to_bittok (USHORT* tokmap)
{	/* Set EOS bit so we don't have to check it explicitly. */
    tokmap [0] |= 1;
    while  ( ! bitlookup (tokmap, last_token) )  {
	get_token ();
    }
}
