/* $Header:readpgm.c 12.0$ */
/* $ACIS:readpgm.c 12.0$ */
/* $Source: /ibm/acis/usr/src/lib/c2_ca/RCS/readpgm.c,v $ */

#ifndef lint
static char *rcsid = "$Header:readpgm.c 12.0$";
#endif

/* READPGM.C */


/*
     The input is broken into the following components:

     o   label(s)
     o   opcode
     o   operand1
     o   operand2 (two parts)
     o   operand3 (two parts)
     o   rest of statement (including comments)

     Not all parts are always present.  Only those opcodes which
     are of interest have operands scanned.  All of those have a
     one part first operand (a register or label) and possibly a
     one or two part second operand and third operand.

     Each statement is further categorized by its type:

     o   unimportant  (such as comments)
     o   dangerous (does things which optimizer doesn't recognize)
     o   load
     o   unconditional branch
     o   conditional branch
     o   branch with execute (either conditional or not)
     o   references (a) register
     o   changes a register

     and others.

     Operands are also flagged by type:

     The output is assumed to be from RUCC. There is no attempt to
     optimize hand written code.  ROPT knows the forms of registers
     (r0,r1,...,r15,sp,...), how labels are written, that no
     branch label is ever referenced in a literal, and other
     things.

*/

#include "stdio.h"
#include "opt.h"
#include "error.h"
#include "instps.h"

#define eatblanks(s)   while((*s)==' ' || (*s)=='\t') s++;

static char token[500] = "\0";
static char *tp;
static int  tlen = 0;

#define LLINE 520
static char line[LLINE];
static char *lp;


ReadProgram()
{
   reclaim();
   MoreToRead = FALSE;
   WhoRunning = who_nobody;    /* i.e. no opt passes running */

   while( fgets(line, LLINE, stdin) )  {

      if( DEBUG2 )  {
         if(Tail!=NULL)   PrintNode( Tail, "" );
         fprintf(stderr,"\n*****************************\n");
         }
      else if (DEBUG1)
         fprintf(stderr,"Line: %s", line); 

      if( Tail != NULL )  {
         if( Tail->labels == NULL &&
             Tail->opcode == NULL &&
             !UNKNOWN(Tail) )
            Tail->type = type_unimportant;
         if( FREGS(Tail) )
            Tail->fregs = FregEval( Tail->op1 );
         }

      lp = line;
      genNew();
      if (!Running)  return;

      if ( scanComment() )    /* true if no more to do */
         continue;
      if (!Running)  return;

      if ( scanLabels() )    /* true when no more to do */
         continue;
      if (!Running)  return;
      if ( scanComment() )
         continue;

      if (!*token) continue;
      genOpCode(token);
      if (!Running)  return;

      if ( UNIMPORTANT(Tail) && ! OPTPSEUDO(Tail) )  {
         genAsIs(lp);
         if (!Running)  return;
         continue;
         }

      scanOperands();
      if (!Running)   return;

      if (*lp!='\0')  genAsIs(lp);

      if ( Tail->type == 0)
         Tail->type = type_unknown;

      if( OPNUMBER(Tail) == i_efunct )
         {  MoreToRead = TRUE;  c.functions+=1;  return;  }
   }
}



static scanToken()
{
   tp = token;
   tlen = 0;
   while( (*lp != '\n') &&
          (*lp != '\t')  &&
          (*lp != '\r')  &&
          (*lp != '|')  &&
	  (*lp != '#') && /* (ef) 1/24/86 -- new comment character */
          (*lp != ' ')  ) {
      *tp++ = *lp++;
      tlen++;
      }
   *tp = '\0';
   if(DEBUG2) fprintf(stderr,"Token='%s'\n", token);
}

/***********************************************************/


static scanComment()
{
   eatblanks(lp);
   if ((*lp=='|')||(*lp=='#')) {
      genComment(line);
      return TRUE;
      }
   return FALSE;
}



static scanLabels()
{
   eatblanks(lp);
   scanToken();
   eatblanks(lp);
   if (*lp == '=')  {
      genAsIs(line);
      return TRUE;
      }
   while (token[tlen-1]==':')  {
      genLabel(token);
      eatblanks(lp);
      if ((*lp == '|')||(*lp=='#'))
         return TRUE;
      scanToken();
      }
   return FALSE;
}



static scanOperands()
{
   int op2parts;
   scanOpToken();
   eatblanks(lp);
   genOp1(token);
   if (*lp != ',')    {
      if (*lp!='|' && *lp!='#' && *lp!='\n')  Error(E_DELIMITER, lp, Tail );
      return;
      }

   /* operand 2 */
   lp++;
   scanOpToken();
   genOp2a(token, &(Tail->op2a) );
   if(*lp == '\n')    return;
   op2parts = 1;

   if (*lp == '(')  {
      op2parts = 2;
      lp++;
      scanOpToken();
      eatblanks(lp);
      if (*lp++ != ')')   { Error(E_DELIMITER, lp, Tail); return; }
      genOp2b(token, &(Tail->op2b) );
      }

   /* see if comment */
   eatblanks(lp);
   if ((*lp == '|')||(*lp == '#'))  { genAsIs(lp);  return; }
   if(*lp == '\n')    return;

   /* operand 3 */
   lp++;
   scanOpToken();
   genOp2a(token, &(Tail->op3a) );
   if(*lp == '\n')    return;

   if (*lp == '(')  {
      if( op2parts==2 )
         {Error(E_DELIMITER, lp, Tail); return; }
      lp++;
      scanOpToken();
      if (*lp++ != ')')   { Error(E_DELIMITER, lp, Tail); return; }
      genOp2b(token, &(Tail->op3b) );
      }
}



static scanOpToken()
{
   tp = token;
   tlen = 0;
   eatblanks(lp);
/* if(*lp=='!')  {  /*literal*/
/*    while( (*lp != '\n') &&
/*           (*lp != '\t')  &&
/*           (*lp != '\r')  &&
/*           (*lp != '|')  ) {
/*       *tp++ = *lp++;
/*       tlen++;
/*       }
*/
   if(*lp=='$')  {  /*literal*/
      *tp++ = *lp++;
      tlen++;
      if(*lp=='.') {
         while( (*lp != '\n') &&
                (*lp != '\t')  &&
                (*lp != '\r')  &&
                (*lp != '#')  &&
                (*lp != '|')  ) {
            *tp++ = *lp++;
            tlen++;
         }
      }
   }
   while( (*lp != '\n') &&
          (*lp != '(')  &&
          (*lp != ')')  &&
          (*lp != ',')  &&
          (*lp != '#')  &&
          (*lp != '|')  ) {
      *tp++ = *lp++;
      tlen++;
      }
   tp--;
   while ((*tp == ' ') || (*tp == '\t')) {
	tlen--;   /* remove trailing blanks */
	tp--;
	}
   *++tp = '\0';
   if(DEBUG2)  fprintf(stderr,"OpToken='%s'\n", token);
}


/***********************************************************/


/* Starting a new input line
   Allocate a new block for it.
*/
static genNew()
{
   struct snode *NewNode();

   register struct snode *new;
   new = NewNode();
   if (new != NULL)  {
      InsertNode( new, Tail );
      Tail->lineno = ReadLineno+=1;
      Tail->seqno = ReadLineno;
      }
}



/* Found a 'standalone' comment:  |   line nn, ...
 * or...			  # blah blah blah (ef) 1/24/86 
*/
static genComment(sp)
register char *sp;
{
   char *cp;

   Tail->type = type_unimportant;
   cp = Myalloc( strlen(sp) );
   if (cp == NULL) {
      Error( E_STORAGE, "(in genComment)", Tail );  Running = FALSE;  return; }
   Tail->rest = cp;
   while (*sp)   *cp++ = *sp++;
   if(*(cp-1) == '\n') cp--;
   *cp = '\0';
}


/* Literal text which goes out as is.
   This might be a comment or an operation
   (such as '.long' or '.using')
   which we don't care about.
*/
static genAsIs(sp)
register char *sp;
{
   register char *cp;

   if (*sp!='\0')  {
      cp = Myalloc( strlen(sp) );
      if (cp == NULL) {Error( E_STORAGE,"(in genAsIs)", Tail);
                       Running=FALSE; return;}
      Tail->rest = cp;
      while (*sp)   *cp++ = *sp++;
      if(*(cp-1) == '\n') cp--;
      *cp = '\0';
      }
   /*PrintNode (Tail, "New AsIs");*/
}


/* Found a label.  Add it to the current statement.
*/
#define MAX_LABELS   50
#define MAX_LAB_MSG "50"
static genLabel(sp)
register char *sp;
{
   register char *cp;
   register int i=0;

   cp = Myalloc( MAX_LABELS );
   if (cp == NULL) {
      Error(E_STORAGE,"(in genLabel)",Tail); Running=FALSE; return;}
   Tail->labels = cp;

   while (*sp && i<MAX_LABELS)  { *cp++ = *sp++; i++; }
   if (i>=MAX_LABELS && *sp)
      Error( E_LABELS, MAX_LAB_MSG, Tail );
   *cp = '\0';
   Tail->type |= type_haslabel;

   eatblanks(lp);
   if( *lp!='\n' && *lp!='|' && *lp!='#' )
      genNew();  /* force label to be on line by itself */
}


/* Found an opcode.
*/
static genOpCode(sp)
register char *sp;
{
   struct opStat *OpcodeEval();

   Tail->opcode = OpcodeEval( sp );
   if (Tail->opcode != NULL)  {
      Tail->type   = Tail->opcode->optype;
      Tail->ldstype = Tail->opcode->opldstype;
      }
   else
      Tail->type = type_unknown;
}



/* Generate Operand One
*/
static genOp1(sp)
register char *sp;
{
   register char *cp;

   if (!strlen(sp))  return;
   cp = Myalloc( strlen(sp) );
   if (cp == NULL) {
      Error(E_STORAGE,"(in genOp1)", Tail); Running=FALSE; return;}
   Tail->op1 = cp;
   while (*sp)   *cp++ = *sp++;
   *cp = '\0';
}



/* Generate Operand Two or Three (first part)
*/
static genOp2a(sp, place)
register char *sp;      /* operand */
register char **place;  /* where to put operand */
{
   register char *cp;

   if (!strlen(sp))  return;
   cp = Myalloc( strlen(sp) );
   if (cp == NULL) {
      Error(E_STORAGE, "(in genOp2a)", Tail); Running=FALSE; return;}
   *place = cp;
   while (*sp)   *cp++ = *sp++;
   *cp = '\0';
}



/* Generate Operand Two or Three (second part)
*/
static genOp2b(sp, place)
register char *sp;      /* operand */
register char **place;  /* where to put operand */
{
   register char *cp;

   if (!strlen(sp))  return;
   cp = Myalloc( strlen(sp) );
   if (cp == NULL) {
      Error(E_STORAGE, "(in genOp2b)", Tail); Running=FALSE; return; }
   *place = cp;
   while (*sp)   *cp++ = *sp++;
   *cp = '\0';
}


/***********************************************************/

/* interface between rest of ROPT and STORAGE.C */

char *Myalloc(size)
int size;
{
   char *alloc_stg();
   size += 1;
   return alloc_stg( size, size>(sizeof(char*)-1) );
   /* 2nd parm of alloc_stg() is TRUE if alignment is needed.
      The expression above forces alignment when requested
      size is equal or greater than systems pointer size.  */
}

static reclaim()
{
   init_storage();
}
