/***********************************************************************
*
* Parse - SLR(1) parser
*
* This routine interprets the parse tables, generated by CHAT, to
* perform the SLR(1) parsing actions.
* Based on Aho and Ullman's parser in "Principles of Compiler Design".
* A lexical scanner is also included to present the input expression
* to the parser as a stream of tokens.
*
* External routines supplied by the user:
*   Parse_Error (error, state);
*
* Files included into the parser:
*   asm7090.sem    - From CHAT
*   asm7090.ptb    - From CHAT
*   asm7090.err    - From CHAT
*   asm7090.tok    - From CHAT
*
*   errors.h      - User produced, Scanner errors
*   scanner.h     - User produced, Scanner actions
*   stables.h     - User produced, Scanner state tables
*
* External State machine tables:
*   parsetable    - From CHAT, in asm7090.ptb
*   gototable     - From CHAT, in asm7090.ptb
*   chartable     - User produced, in stables.h
*   scantable     - User produced, in stables.h
*
* Changes:
*   LongAgo    DGP   Original.
*   03/11/05   DGP   Fixed qualsym processing.
*
***********************************************************************/
 
/* System includes */
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#ifndef TRUE
#define TRUE 1
#endif
 
#ifndef FALSE
#define FALSE 0
#endif
 
 
/* Scanner actions */
 
#define NULL_ACTION     0x00
#define ERROR_ACTION    0x01
#define BACK_ACTION     0x02
#define MOVE_ACTION     0x04
#define EAT_ACTION      0x08
#define BUILD_ACTION    0x10
 
/* Parser actions */
 
#define SHIFT_ACTION    0
#define REDUCE_ACTION   1
 
/* Local types */
 
#include "asmdef.h"
#include "asmdmem.h"
#include "errors.h"
#include "asmfps.h"

extern t_uint64 ptens[];
extern t_uint64 mtens[];
 
/* Stack element */
 
typedef struct selement
{
   struct selement *link;
   pstate   state;
   tokval   svalue;
   char     cvalue[TOKENLEN+2];
   char     qvalue[TOKENLEN+2];
} selement_t ;
 
/* Parser tables */
 
#include "asm7090.ptb"
 
/* Scanner tables */
 
#include "stables.h"
 
/* Local static variables */
 
static selement_t *stack;
static selement_t *frees = NULL;
static int err_flag;
static int relocatable;
static int defer;
static int pcinc;
static int entsize;
 
/* External variables */
 
extern int pc;
extern int symbolcount;
extern int exprtype;
extern int termparn;
extern int rightmargin;
extern int linenum;
extern int genxref;
extern int absmod;
extern int filenum;
extern int inpass;
extern int errnum;
extern int radix;
extern int absolute;
extern int pgmlength;
extern int qualindex;
extern int addext;
extern int rboolexpr;
extern int lboolexpr;
extern int fapmode;

extern char inbuf[MAXLINE];
extern char errline[10][120];
extern char qualtable[MAXQUALS][MAXSYMLEN+2];

extern SymNode *symbols[MAXSYMBOLS];
 
/***********************************************************************
*
* POP - POP parser stack
* This routine pops parse states and token values from the parse stack
* when a reduction is recognized. Put pop'd elements onto a free stack
* for speed.
*
***********************************************************************/

static void
Pop (int h)
{
   selement_t *stemp;
   int i;
 
#ifdef DEBUG_PARSE
   printf ("Pop: h = %d\n",
	   h);
#endif

   for (i=0; i<h; i++)
   {
      if (stack == NULL) break;
      else
      {
         stemp = stack->link;
         stack->link = frees;
         frees = stack;
         stack = stemp;
      }
   } /* of for */
 
} /* Pop */
 
/***********************************************************************
*
* PUSH - Push parser stack
* This routine pushes a parse state and token value onto the parse
* stack. Allocate new elements if free stack is empty.
*
***********************************************************************/

static void
Push (pstate s,
      tokval v,
      char *cv,
      char *qv)
{
   selement_t *stemp;
 
#ifdef DEBUG_PARSE
   printf ("Push: state = %d, tokval = %d, toksym = '%s', tokqual = '%s'\n",
	   s,
	   v,
	   cv ? cv : "null",
	   qv ? cv : "null");
#endif

   if (frees == NULL)
   {
      stemp = (selement_t *)DBG_MEMGET (sizeof(selement_t));
      if (stemp == NULL)
      {
         Parse_Error (MEM_OVERFLOW, STACK_OVERFLOW, defer);
         return;
      }
   }
   else
   {
      stemp = frees;
      frees = stemp->link;
   }
 
   memset (stemp, '\0', sizeof(selement_t));
   stemp->state = s;
   stemp->svalue = v;
   if (cv)
   {
      if (strlen(cv) > TOKENLEN)
      {
#ifdef DEBUGMALLOCS
         printf ("Push: token cv too long: cv = %s\n", cv);
#endif
         cv[TOKENLEN] = '\0';
      }
      strcpy (stemp->cvalue, cv);
   }
   if (qv)
   {
      if (strlen(qv) > TOKENLEN)
      {
#ifdef DEBUGMALLOCS
         printf ("Push: token qv too long: qv = %s\n", qv);
#endif
         qv[TOKENLEN] = '\0';
      }
      strcpy (stemp->qvalue, qv);
   }
   stemp->link  = stack;
   stack = stemp;
 
} /* Push */
 
/***********************************************************************
*
* Stkval - Get stack value
* This routine returns the value of a stack element given its position.
*
***********************************************************************/

static tokval
Stkval (int depth)
{
   selement_t *stemp;
   int i;
 
#ifdef DEBUG_INTERP
   printf ("Stkval: depth = %d\n",
	   depth);
#endif

   stemp = stack; /* Find stack element */
   for (i=2; i <= depth; i++)
   {
      stemp = stemp->link;
      if (stemp == NULL) return (VALUEZERO);
   }
#ifdef DEBUG_INTERP
   printf ("   value = %d\n",
	   stemp->svalue);
#endif
   return (stemp->svalue);
 
} /* Stkval */
 
/***********************************************************************
*
* CStkval - Get char stack value
* This routine returns a pointer to the character value of a stack
* element given its position.
*
***********************************************************************/

static char *
CStkval (int depth)
{
   selement_t *stemp;
   int i;
 
#ifdef DEBUG_INTERP
   printf ("CStkval: depth = %d\n",
	   depth);
#endif

   stemp = stack; /* Find stack element */
   for (i=2; i <= depth; i++)
   {
      stemp = stemp->link;
      if (stemp == NULL) return (NULL);
   }
#ifdef DEBUG_INTERP
   printf ("   value = '%s'\n", stemp->cvalue);
#endif
   return (stemp->cvalue);
 
} /* CStkval */
 
/***********************************************************************
*
* QStkval - Get qual stack value
* This routine returns a pointer to the character value of a stack
* element given its position.
*
***********************************************************************/

static char *
QStkval (int depth)
{
   selement_t *stemp;
   int i;
 
#ifdef DEBUG_INTERP
   printf ("QStkval: depth = %d\n",
	   depth);
#endif

   stemp = stack; /* Find stack element */
   for (i=2; i <= depth; i++)
   {
      stemp = stemp->link;
      if (stemp == NULL) return (NULL);
   }
#ifdef DEBUG_INTERP
   printf ("   qvalue = '%s'\n", stemp->qvalue);
#endif
   return (stemp->qvalue);
 
} /* QStkval */
 
/***********************************************************************
*
* TOP - Get current parse state
* This routine return the parse state from the top element of the
* parse stack.
*
***********************************************************************/

static pstate
Top (void)
{
   return (stack->state);
}
 
/***********************************************************************
*
* Interpret - Interpret syntactical reduction
* This routine adds the semantic interpretation to the recognition of
* syntactical reductions.
*
***********************************************************************/

static tokval
Interpret (pstate r)
{
   tokval value;
   char symbol[TOKENLEN+2];
   char qualsym[TOKENLEN+2];
 
#ifdef DEBUG_INTERP
   printf ("Interpret: red = %d\n",
	   r);
#endif
 
   value = VALUEZERO;
   symbol[0] = '\0';
   qualsym[0] = '\0';
   switch (r)
   {
 
   /* Get the generated semantic actions */
#include "asm7090.sem"
 
   default:
      value = Stkval(1);
   }
 
   return (value);
 
} /* Interp */
 
/***********************************************************************
*
* Scanner - Lexical scanner
* This routine is a table driven scanner used to lexically analyze
* source input. Scanner is called whenever the parser needs the next
* token in the input stream. The scanner is implemented as a finite
* state machine.
*
***********************************************************************/

toktyp
Scanner (char expr[],
         int *ndx,
         tokval *svalue,
	 char *toksym,
	 char *tokqual,
	 char *term,
	 int defr)
{
   tokval dignum, value;
   long   evalue;
   long   expsgn, sexp, sfrc;
   int    dblprecision;
   int    i, sdx;
   int    digitfound, leadingzero;
   uint16 sa, *pt;
   toktyp token;
   uint8  chr, next, action;
   uint8  select;
   uint8  current_state; /* Scanner current state */
   char   lachar, latran; /* Look ahead character */
   char   symbol[TOKENLEN+2]; /* Collected symbol */
   char   qualsym[TOKENLEN+2]; /* Collected symbol qualifier */
 
#ifdef DEBUG_SCAN
   printf ("Scanner entered:\n");
#endif
 
   defer = defr;
   expsgn = 1;
   value = VALUEZERO; /* Initialization */
   evalue = VALUEZERO; 
   sfrc = VALUEZERO;
   sexp = VALUEZERO;
   sdx = 0;
   leadingzero = 0;
   digitfound = FALSE;
   dblprecision = FALSE;
   symbol[sdx] = '\0';
   qualsym[sdx] = '\0';

   /* Initialize current state */
   if ((exprtype & EXPRTYPEMASK) == DATAEXPR)
      current_state = DATAEXPRSTART;
   else if ((exprtype & EXPRTYPEMASK) == BOOLEXPR)
      current_state = BOOLEXPRSTART;
   else
      current_state = ADDREXPRSTART;
 
   do
   {
 
      lachar = expr[*ndx]; /* Get current input char */
      latran = chartable[lachar]; /* Classify the character */

      if (termparn && (latran == LPARN || latran == RPARN))
      {
         latran = OPERAT;
      }
 
#ifdef DEBUG_SCAN
      printf (" cs = %d, lachar = %02x, latran = %d\n",
	      current_state,
	      lachar,
	      latran);
#endif
 
      dignum = VALUEZERO;
      if (isdigit(lachar)) /* Convert digit to number */
         dignum = lachar - '0';
#ifdef DEBUG_SCAN
      printf (" dignum = %d\n", dignum);
#endif
 
      /* Find state transition given current state and input character */
 
      pt = (uint16 *)scantable[current_state-1];
 
      for (i=0 ;; i++)
      {
 
         sa = *pt++;
         action = sa & 31;
         next = (sa >> 5) & 63;
         chr = (sa >> 11) & 31;

 
#ifdef DEBUG_SCAN
         printf (" Si = %d, a = %02x, n = %d, c = %d\n",
		 i,
		 action,
		 next,
		 chr);
#endif
 
         if ((chr == latran) || (chr == 31)) /* State transition fnd. */
	 {
 
            /* Perform the scan action for this transition */
 
            if (ERROR_ACTION & action) /* Error, terminate scan */
	    {
#ifdef DEBUG_SCAN
               printf ("   ERROR: \n");
#endif
               Parse_Error (SCAN_ERROR, current_state, defer);
               return (0);
            }
 
            if (BACK_ACTION & action) /* Back up in input stream */
	    {
#ifdef DEBUG_SCAN
               printf ("   BACK:\n");
#endif
               *ndx = *ndx - 1;
            }
 
            if (EAT_ACTION & action) /* Eat (ignore) character */
	    {
#ifdef DEBUG_SCAN
               printf ("   EAT:\n");
#endif
               *ndx = *ndx + 1;
            }
 
            if (MOVE_ACTION & action) /* Move to symbol */
	    {
#ifdef DEBUG_SCAN
               printf ("   MOVE: \n");
#endif
               if (sdx < TOKENLEN)
	       {
		  symbol[sdx] = lachar;
                  sdx ++;
                  symbol[sdx] = '\0';
               }
            }
 
            if (BUILD_ACTION & action) /* Token found process it */
	    {
	       *term = lachar;
               if (next == 0) select = current_state;
               else           select = next;
#ifdef DEBUG_SCAN
               printf ("   BUILD: select = %d\n",
		       select);
#endif
               switch (select)
	       {
 
               /* Get the scanner actions */
#include "scanner.h"
 
               }
            }
 
            current_state = next;      /* Goto new scan state */
            break;
 
         }
      }
 
   } while (current_state != 0);
 
#ifdef DEBUG_SCAN
   printf (" token = %d, val = %d, symbol = '%s', qual = '%s'\n",
	   token,
	   value,
	   symbol[0] ? symbol : "null",
	   qualsym[0] ? symbol : "null");
#endif
 
   *svalue = value;
   strcpy (toksym, symbol);
   strcpy (tokqual, qualsym);
 
   return (token);
 
} /* Scanner */
 
/***********************************************************************
*
* Parser - SLR(1) parser
* This routine interprets the parse tables to perform the SLR(1)
* parsing actions.
*
***********************************************************************/

tokval
Parser (char *expr, int *relo, int *eindex, int entsz, int defr, int pinc)
{
   uint16 *pt, *gp;
   tokval value, rvalue;
   int    i, j, index;
   uint16 pa, tok, act;
   toktyp token;
   pstate current_state, c_s;
   pstate next, crnt;
   pstate sr;
   char   tterm;
   char   toksym[TOKENLEN+2];
   char   tokqual[TOKENLEN+2];
 
#ifdef DEBUG_PARSE
   printf ("Parse Entered:\n");
#endif
 
   entsize = entsz;
   defer = defr;
   pcinc = pinc;
   index = 0;
   relocatable = 0;
   current_state = 1;
   stack = NULL;
   err_flag = FALSE;
   rboolexpr = FALSE;
   lboolexpr = FALSE;
   toksym[0] = '\0';
   tokqual[0] = '\0';
 
   Push (current_state, VALUEZERO, NULL, NULL);

   /* Get look ahead input token */
   token = Scanner (expr, &index, &value, toksym, tokqual, &tterm, defer);
   if (token == 0) return(0);
 
   do
   {
 
      /* Get action entry for current state, look ahead token */
 
      pt = (uint16 *)parsetable[current_state-1];
 
      for (i=0 ;; i++)
      {
 
         pa = *pt++;
         tok = pa & 127;
         act = (pa >> 7) & 1;
         sr = (pa >> 8) & 255;
 
#ifdef DEBUG_PARSE
         printf (" Pi = %d, t = %d, a = %d, sr = %d\n",
		 i,
		 tok,
		 act,
		 sr);
#endif
 
         if ((tok == 127) || (tok == token))
	 {
 
            /* State action found - do error, shift or reduce action */
 
            if ((sr == 255) || err_flag)
	    {
               if (! err_flag) /* Error processor - user defined */
                  Parse_Error (PARSE_ERROR, current_state, defer);
               Pop (1000);
	       if (!absmod && !absolute && relocatable == 1) *relo = TRUE;
	       else *relo = FALSE;
	       *eindex = index;
               return (value);
            }
 
            if (act == SHIFT_ACTION)
	    {
 
#ifdef DEBUG_PARSE
               printf (" Shift, value = %d\n",
		       value);
#endif
 
               Push (sr, value, toksym, tokqual);
               token = Scanner (expr, &index, &value, toksym, tokqual, &tterm,
				defer);
	       if (token == 0) return(0);
            }
 
            else /* REDUCE_ACTION */
	    {
               rvalue = Interpret (sr);
 
#ifdef DEBUG_PARSE
               printf (" Reduce, sr = %d, rvalue = %d\n",
		       sr,
		       rvalue);
               printf (" Handle = %d\n",
		       gototable[sr-1].handle);
#endif
 
               Pop (gototable[sr-1].handle);
               c_s = Top();
 
#ifdef DEBUG_PARSE
               printf (" TOP-state = %d\n",
		       c_s);
#endif
 
               /* Use goto tables to get next state */
 
               gp = (uint16 *) gototable[sr-1].go;
 
               for (j=0 ;; j++)
	       {
 
                  pa = *gp++;
                  crnt = pa & 255;
                  next = (pa >> 8) & 255;
 
#ifdef DEBUG_PARSE
                  printf (" j = %d, crnt = %d, next = %d\n",
			  j,
			  crnt,
			  next);
#endif
 
                  if ((crnt == c_s) || (crnt == 255))
		  {
                     Push (next, rvalue, NULL, NULL);
                     break;
                  }
 
               } /* for j */
 
            }
            break;
         }
 
      } /* for i */
 
      current_state = Top();
 
#ifdef DEBUG_PARSE
      printf (" Cursta = %d\n",
	      current_state);
#endif
 
   } while (current_state != 0); /* Until input is accepted */
 
   Pop (50); /* Purge stack of possible leftovers */
 
   if (!absmod && !absolute && relocatable == 1) *relo = TRUE;
   else *relo = FALSE;
   *eindex = index;

   if (exprtype & DATAVALUE) ;
   else if (exprtype & BOOLVALUE) rvalue &= 0777777;
   else                           rvalue &= 0077777;

   return (rvalue);
 
} /* Parser */
