/*--------------------------------------------------------------*/
/*

 TinC - Tiny, in C
 tinc10.c
 Dec 2004, Peter F. Gray

 Extrapolated from Tiny 1.1 coded by Jack W. Crenshaw, in Pascal,
 as part of Jack's "Let's Build A Compiler" tutorial.

*/
/*--------------------------------------------------------------*/

#include "tinc.h"

#include <stdio.h>

FILE *ip = NULL;		// input (source) file
FILE *op = NULL;		// output file
FILE *op1 = NULL;		// output file 1
FILE *op2 = NULL;		// output file 2
FILE *tr = NULL;		// trace file

int tracing = TRUE;		// tracing flag
int opfile1 = TRUE;		// output file flag 1
int opfile2 = FALSE;		// output file flag 2

int interactive = 0;		// interactive flag
int trindent = 0;		// trace indent level
int linenumber = 0;		// current source line number

static void BoolExpression();
static void Block();


/*--------------------------------------------------------------*/
/* Symbol Tables etc	*/

static Symbol GS_Name[MAXGLOBALS];	// global symbol table (name)
static char   GS_Type[MAXGLOBALS];	// data type
static int    GS_Size[MAXGLOBALS];	// size (in 'data type' units)

static Symbol LS_Name[MAXLOCALS];	// local symbol table (name)
static char   LS_Type[MAXLOCALS];	// data type
static int    LS_Size[MAXLOCALS];	// size (in 'data type' units)

static long NumGlobals = 0;		// Number of globals used
static long NumLocals = 0;		// Number of locals used
static int  TotLocSize;			// Space required for locals

static long LCount = 0;			// Label Count


/*--------------------------------------------------------------*/
/* Definition of Keywords, etc */

static char Look;			// Lookahead character
static char Token;			// Encoded Token       
static char Value[STRTMPSIZE];		// Unencoded Token     


/*--------------------------------------------------------------*/
/* Staging Buffer */

struct sbuff_struct {
  int pcode;
  char s1[MAXSYMSIZE];
  char s2[MAXSYMSIZE];
  char s3[MAXSYMSIZE];
} sbuff[MAXSTAGE];

long int sbufftop = 0;


/*--------------------------------------------------------------*/
/* Dump the symbol table */

static void dump_symbol()
{
  int tmp;
  char STR1[STRTMPSIZE];

  if (!tracing) return;
  sprintf (STR1,"%%3d %%%ds %%c %%d\n",MAXSYMSIZE);
  fprintf (tr,"\nSymbol Table\n\n");
  for (tmp=0; tmp<NumGlobals; tmp++) {
    fprintf (tr,STR1, tmp, GS_Name[tmp], GS_Type[tmp], GS_Size[tmp]);
  }
  fprintf (tr,"\n");

  if (NumLocals>0) {
    fprintf (tr,"\nLocal Table\n\n");
    for (tmp=0; tmp<NumLocals; tmp++)
      fprintf (tr,STR1, tmp, LS_Name[tmp], LS_Type[tmp], LS_Size[tmp]);
    fprintf (tr,"\n");
  }
}


/*--------------------------------------------------------------*/
/* Dump the Staging Buffer */

static void dump_pcodes ()
{
  int tmp;
  char STR1[STRTMPSIZE];

  if (!tracing) return;
  if (sbufftop>0) {
    sprintf (STR1,"%%5d %%3d %%%ds %%%ds %%%ds\n",MAXSYMSIZE,MAXSYMSIZE,MAXSYMSIZE);
    fprintf (tr,"\nPCODES\n\n");
    for (tmp=0; tmp<sbufftop; tmp++) {
      fprintf (tr,STR1, tmp, sbuff[tmp].pcode,sbuff[tmp].s1,sbuff[tmp].s2,sbuff[tmp].s3);
    }
  }
}


/*--------------------------------------------------------------*/
/* Compiler trace routine */

static void trace (char porm, char *routine, char *p1, char *p2, int p3, int p4)
{
  int i;
  if (tracing) {
    if (porm == '-') trindent--;
    for (i=0; i<trindent; i++) fprintf (tr," ");
    if (p1[0] == 0)
      fprintf (tr,"%c%s\n",porm,routine);
    else
      fprintf (tr,"%c%s [%s]\n",porm,routine,p1);

    if (porm == '+') trindent++;
  }
}


/*--------------------------------------------------------------*/
/* Write the pcode to the Staging Buffer */

static void EmitLn();
static void EmitLn_notab();
static void Abort();


static void Stage (int pcode, char *s1, char *s2, char *s3)
{
  sbuff[sbufftop].pcode = pcode;
  strcpy (sbuff[sbufftop].s1,s1);
  strcpy (sbuff[sbufftop].s2,s2);
  strcpy (sbuff[sbufftop].s3,s3);
  sbufftop++;
}


/*--------------------------------------------------------------*/
/* Generate code for a Staging Buffer item */

static void Gen_Item (int pcode, char *s1, char *s2, char *s3)
{
  char strtmp1[STRTMPSIZE];

  switch (pcode) {
  case PC_LABEL:
    sprintf(strtmp1,"%s:",s1);
    EmitLn_notab(strtmp1);
    break;
  case PC_CLEAR_PRIMARY:
    EmitLn("Clear Primary");
    break;
  case PC_NEGATE_PRIMARY:
    EmitLn("Negate Primary");
    break;
  case PC_NOT_PRIMARY:
    EmitLn("NOT Primary");
    break;
  case PC_MOVE_CONST_PRIMARY:
    sprintf(strtmp1,"Move constant '%s' to Primary",s1);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_VAR_PRIMARY:
    sprintf(strtmp1,"Move global variable '%s' to Primary",s1);
    EmitLn(strtmp1);
    break;
  case PC_PUSH_PRIMARY:
    EmitLn("Push Primary");
    break;
  case PC_POPADD_PRIMARY:
    EmitLn("Add TOS to Primary");
    break;
  case PC_POPSUB_PRIMARY:
    EmitLn("Subtract Primary from TOS");
    break;
  case PC_POPMUL_PRIMARY:
    EmitLn("Multiply TOS by Primary");
    break;
  case PC_POPDIV_PRIMARY:
    EmitLn("Divide TOS by Primary");
    break;
  case PC_POPAND_PRIMARY:
    EmitLn("AND TOS with Primary");
    break;
  case PC_POPOR_PRIMARY:
    EmitLn("OR TOS with Primary");
    break;
  case PC_POPXOR_PRIMARY:
    EmitLn("XOR TOS with Primary");
    break;
  case PC_POPCMP_PRIMARY:
    EmitLn("Compare TOS with Primary");
    break;
  case PC_SET_PRIMARY_EQ:
    EmitLn("Set Primary if compare was EQ");
    break;
  case PC_SET_PRIMARY_NE:
    EmitLn("Set Primary if compare was NE");
    break;
  case PC_SET_PRIMARY_LT:
    EmitLn("Set Primary if compare was LT");
    break;
  case PC_SET_PRIMARY_GT:
    EmitLn("Set Primary if compare was GT");
    break;
  case PC_SET_PRIMARY_GE:
    EmitLn("Set Primary if compare was GE");
    break;
  case PC_SET_PRIMARY_LE:
    EmitLn("Set Primary if compare was LE");
    break;
  case PC_MOVE_PRIMARY_VAR:
    sprintf(strtmp1,"Move Primary to global variable '%s'",s1);
    EmitLn(strtmp1);
    break;
  case PC_BRANCH:
    sprintf(strtmp1,"Branch to %s",s1);
    EmitLn(strtmp1);
    EmitLn("");
    break;
  case PC_BRANCH_FALSE:
    sprintf(strtmp1,"Branch to %s if false",s1);
    EmitLn(strtmp1);
    EmitLn("");
    break;
  case PC_HEADER:
    EmitLn("Header");
    break;
  case PC_PROLOG:
    EmitLn("Prolog");
    break;
  case PC_EPILOG:
    EmitLn("Epilog");
    break;
  case PC_ALLOCATE:
    sprintf(strtmp1,"Allocate %s %s",s1,s2);
    EmitLn(strtmp1);
    break;
  case PC_GROWSTACK:
    sprintf(strtmp1,"Grow Stack %s",s1);
    EmitLn(strtmp1);
    break;
  case PC_SHRINKSTACK:
    sprintf(strtmp1,"Shrink Stack %s",s1);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_LVAR_PRIMARY:
    sprintf(strtmp1,"Move variable '%s' (SF offset %s) to Primary",s1,s2);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_PRIMARY_LVAR:
    sprintf(strtmp1,"Move Primary to variable '%s' (SF offset %s)",s1,s2);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_A_PRIMARY:
    sprintf(strtmp1,"Move address of global variable '%s' to Primary",s1);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_AL_PRIMARY:
    sprintf(strtmp1,"Move address of variable '%s' to Primary",s1);
    EmitLn(strtmp1);
    break;
  case PC_POP_SECONDARY:
    EmitLn("Pop Secondary");
    break;
  case PC_ADJUST_SECONDARY:
    sprintf(strtmp1,"Adjust Secondary by data size of '%s' (%s)",s1,s2);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_SI_PRIMARY:
    EmitLn("Move [Secondary] to Primary");
    break;
  case PC_ADD_PRIMARY_SEC:
    EmitLn("Add Primary to Secondary");
    break;
  case PC_MOVE_PRIMARY_SI:
    EmitLn("Move Primary to [Secondary]");
    break;
  case PC_PUSH_SECONDARY:
    EmitLn("Push Secondary");
    break;
  case PC_PRIMARY_SECONDARY:
    EmitLn("Move Primary to Secondary");
    break;
  default:
    sprintf(strtmp1,"Unknown pcode (%d)",pcode);
    Abort(strtmp1);
    break;
  }
}


/*--------------------------------------------------------------*/
/* Process the Staging Buffer and generate assembly code */

static void Gen_Code (FILE *thisop)
{
  FILE *saved_op;

  saved_op = op;
  op = thisop;
  long int tmp;
  for (tmp=0; tmp<sbufftop; tmp++) {
    Gen_Item (sbuff[tmp].pcode,sbuff[tmp].s1,sbuff[tmp].s2,sbuff[tmp].s3);
  }
  EmitLn("");
  EmitLn("");
  op = saved_op;
}


/*--------------------------------------------------------------*/
/* Staging buffer optimizer (code according to target) */

static void Optimize ()
{

}


/*--------------------------------------------------------------*/
/* Read New character From Input Stream */

static void Getchar()
{
  if (ip == NULL) Look = getchar(); else Look = fgetc(ip);
  if (Look == LF) linenumber++;
}


/*--------------------------------------------------------------*/
/* Report an Error */

static void Error(char *s)
{
  char STR1[STRTMPSIZE];
  interactive = TRUE;
  sprintf(STR1,"Error, line %d : %s.",linenumber,s);
  EmitLn_notab (STR1);
}


/*--------------------------------------------------------------*/
/* Return the size (in base units) of a standard data type */

static int SizeOfType (char datatype)
{
  switch (datatype) {
  case TOK_CHARDEF:
    return SIZEOFCHAR;
  case TOK_INTDEF:
    return SIZEOFINT;
  default:
    Abort ("Unknown Data Type");
  }
}


/*--------------------------------------------------------------*/
/* Report Error and Halt */

static void Abort(char *s)
{
  Error(s);
  dump_symbol();
  exit(EXIT_FAIL);
}


/*--------------------------------------------------------------*/
/* Report What Was Expected */

static void Expected(char *s)
{
  char STR1[STRTMPSIZE];

  sprintf(STR1, "%s Expected", s);
  Abort(STR1);
}


/*--------------------------------------------------------------*/
/* Report an Undefined Identifier */

static void Undefined(char *n)
{
  char STR1[STRTMPSIZE];

  sprintf(STR1, "Undefined Identifier %s", n);
  Abort(STR1);
}


/*--------------------------------------------------------------*/
/* Report a Duplicate Identifier */

static void Duplicate(char *n)
{
  char STR1[STRTMPSIZE];

  sprintf(STR1, "Duplicate Identifier %s", n);
  Abort(STR1);
}


/*--------------------------------------------------------------*/
/* Check to Make Sure the Current Token is an Identifier */

static void CheckIdent()
{
  if (Token != TOK_IDENTIFIER) Expected("Identifier");
}


/*--------------------------------------------------------------*/
/* Recognize an Alpha character */

static int IsAlpha(char c)
{
  return isupper(toupper(c));
}


/*--------------------------------------------------------------*/
/* Recognize a Decimal Digit */

static int IsDigit(char c)
{
  return isdigit(c);
}


/*--------------------------------------------------------------*/
/* Recognize an AlphaNumeric character */

static int IsAlNum(char c)
{
  return (IsAlpha(c) | IsDigit(c));
}


/*--------------------------------------------------------------*/
/* Recognize an Addop */

static int IsAddop(char c)
{
  return (c == '-' || c == '+');
}


/*--------------------------------------------------------------*/
/* Recognize a Mulop */

static int IsMulop(char c)
{
  return (c == '/' || c == '*');
}


/*--------------------------------------------------------------*/
/* Recognize a int Orop */

static int IsOrop(char c)
{
  return (c == '~' || c == '|');
}


/*--------------------------------------------------------------*/
/* Recognize a Relop */

static int IsRelop(char c)
{
  return (c == '>' || c == '<' || c == '=');
}


/*--------------------------------------------------------------*/
/* Recognize White Space */

static int IsWhite(char c)
{
  return (c == LF || c == CR || c == TAB || c == ' ');
}


/*--------------------------------------------------------------*/
/* Skip Over Leading White Space */

static void SkipWhite()
{
  while (IsWhite(Look)) Getchar();
}


/*--------------------------------------------------------------*/
/* Table Lookup */

static long Lookup(Symbol *T, char *s, long n)
{
  long i;
  int found;

  found = FALSE;
  i = n;
  while (i > 0 && !found) {
    if (!strcmp(s, T[i - 1]))
      found = TRUE;
    else
      i--;
  }
  return i;
}


/*--------------------------------------------------------------*/
/* Locate a Symbol in the Global Table */
/* Returns the index of the entry. Zero if not present. */

static long Locate(char *N)
{
  return (Lookup(GS_Name, N, NumGlobals));
}


/*--------------------------------------------------------------*/
/* Locate a Symbol in the Local Table */
/* Returns the index of the entry. Zero if not present. */

static long IsLocal (char *name)
{
  return (Lookup(LS_Name, name, NumLocals));
}


/*---------------------------------------------------------------*/
/* Return the offset of a local within the stack frame */

static int LocalOffset (char *Name)
{
  int offset = 0,i;
  long element;

  element = Lookup(LS_Name, Name, NumLocals);
  for (i=0; i<element-1; i++)
    offset += (LS_Size[i] * SizeOfType(LS_Type[i]));
  return offset;
}


/*--------------------------------------------------------------*/
/* Look for Symbol in Table */

static int InTable(char *n)
{
  return (Lookup(GS_Name, n, NumGlobals) != 0);
}


/*--------------------------------------------------------------*/
/* Check to See if an Identifier is in the Symbol Table         */
/* Report an error if it's not. */


static void CheckTable(char *N)
{
  if (!InTable(N)) Undefined(N);
}


/*--------------------------------------------------------------*/
/* Check the Symbol Table for a Duplicate Identifier */
/* Report an error if identifier is already in table. */

static void CheckDup(char *N)
{
  if (InTable(N)) Duplicate(N);
}


/*--------------------------------------------------------------*/
/* Add a New Entry to Symbol Table */

static void AddEntry(char *N, char T)
{
  trace ('+',"AddEntry",N,"",0,0);
  CheckDup(N);
  if (NumGlobals == MAXGLOBALS) Abort("Symbol Table Full");
  NumGlobals++;
  strcpy(GS_Name[NumGlobals - 1], N);
  GS_Type[NumGlobals - 1] = T;
  trace ('-',"AddEntry","","",0,0);
}


/*--------------------------------------------------------------*/
/* Get an Identifier */

static void GetName()
{
  trace ('+',"GetName","","",0,0);
  SkipWhite();
  if (!IsAlpha(Look)) Expected("Identifier");
  Token = TOK_IDENTIFIER;
  *Value = '\0';
  do {
    sprintf(Value + strlen(Value), "%c", Look);
    Getchar();
  } while (IsAlNum(Look));
  trace ('-',"GetName",Value,"",0,0);
}


/*--------------------------------------------------------------*/
/* Get a Number */

static void GetNum()
{
  trace ('+',"GetNum","","",0,0);
  SkipWhite();
  if (!IsDigit(Look)) Expected("Number");
  Token = TOK_NUM;
  *Value = '\0';
  do {
    sprintf(Value + strlen(Value), "%c", Look);
    Getchar();
  } while (IsDigit(Look));
  trace ('-',"GetNum",Value,"",0,0);
}


/*--------------------------------------------------------------*/
/* Get an Operator */

static void GetOp()
{
  trace ('+',"GetOp","","",0,0);
  SkipWhite();
  Token = Look;
  sprintf(Value, "%c", Look);
  Getchar();
  trace ('-',"GetOp",Value,"",0,0);
}


/*--------------------------------------------------------------*/
/* Get the Next Input Token */

static void Next()
{
  trace ('+',"Next","","",0,0);
  SkipWhite();
  if (IsAlpha(Look)) {
    GetName();
    trace ('-',"Next","","",0,0);
    return;
  }
  if (IsDigit(Look))
    GetNum();
  else
    GetOp();
  trace ('-',"Next","","",0,0);
}


/*--------------------------------------------------------------*/
/* Scan the Current Identifier for Keywords */

static void Scan()
{
  trace ('+',"Scan","","",0,0);
  if (Token == TOK_IDENTIFIER) Token = KWcode[Lookup(KWlist, Value, (long)NKW)];
  trace ('-',"Scan","","",0,0);
}


/*--------------------------------------------------------------*/
/* Match a Specific Input String */

static void MatchString(char *x)
{
  char STR2[STRTMPSIZE];

  if (strcmp(Value, x)) {
    sprintf(STR2, "'%s'", x);
    Expected(STR2);
  }
  Next();
}


/*--------------------------------------------------------------*/
/* Output a String with Tab */

static void Emit(char *s)
{
  if (interactive) printf("%c%s", TAB, s);
  if (op != NULL) fprintf(op,"%c%s", TAB, s);
}


/*--------------------------------------------------------------*/
/* Output a String without Tab */

static void Emit_notab(char *s)
{
  if (interactive) printf("%s", s);
  if (op != NULL) fprintf(op,"%s", s);
}


/*--------------------------------------------------------------*/
/* Output a String with Tab and CRLF */

static void EmitLn(char *s)
{
  Emit(s);
  if (interactive) putchar('\n');
  if (op != NULL) fprintf (op,"\n");
}


/*--------------------------------------------------------------*/
/* Output a String with CRLF, but no TAB */

static void EmitLn_notab(char *s)
{
  Emit_notab(s);
  if (interactive) putchar('\n');
  if (op != NULL) fprintf (op,"\n");
}


/*--------------------------------------------------------------*/
/* Generate a Unique Label */

static char *NewLabel(char *Result)
{
  char S[STRTMPSIZE];

  sprintf(S, "%ld", LCount);
  sprintf(Result, "L%s", S);
  LCount++;
  return Result;
}


/*--------------------------------------------------------------*/
/* Post a Label To Output */

static void PostLabel(char *L)
{
  Stage (PC_LABEL,L,"","");
}


/*---------------------------------------------------------------*/
/* Clear the Primary Register */

static void Clear()
{
  Stage (PC_CLEAR_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* Negate the Primary Register */

static void Negate()
{
  Stage (PC_NEGATE_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* Complement the Primary Register */

static void NotIt()
{
  Stage (PC_NOT_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* Load a Constant Value to Primary Register */

static void LoadConst(char *n)
{
  trace ('+',"LoadConst",n,"",0,0);
  Stage (PC_MOVE_CONST_PRIMARY,n,"","");
  trace ('-',"LoadConst","","",0,0);
}


/*---------------------------------------------------------------*/
/* Return the scope of a variable */
static int WhatIs(char *Name)
{
  if (IsLocal(Name)) return LOCAL;
  if (InTable(Name)) return GLOBAL;
  return UNKNOWN;
}


/*---------------------------------------------------------------*/
/* Load a Variable to Primary Register */

static void LoadVar(char *Name)
{
  int offset;
  char STR1[STRTMPSIZE];

  trace ('+',"LoadVar",Name,"",0,0);
  switch (WhatIs(Name)) {
  case GLOBAL:
    Stage (PC_MOVE_VAR_PRIMARY,Name,"","");
    break;
  case LOCAL:
    offset = LocalOffset(Name);
    sprintf(STR1,"%d",offset);
    Stage (PC_MOVE_LVAR_PRIMARY,Name,STR1,"");
    break;
  case UNKNOWN:
    Undefined(Name);
  default:
    Abort("Unsupported LoadVar mechanism");
  }
  trace ('-',"LoadVar","","",0,0);
}


/*---------------------------------------------------------------*/
/* Store Primary to Variable */

static void Store(char *Name)
{
  int offset;
  char STR1[STRTMPSIZE];

  trace ('+',"Store",Name,"",0,0);
  switch (WhatIs(Name)) {
  case GLOBAL:
    Stage (PC_MOVE_PRIMARY_VAR,Name,"","");
    break;
  case LOCAL:
    offset = LocalOffset(Name);
    sprintf(STR1,"%d",offset);
    Stage (PC_MOVE_PRIMARY_LVAR,Name,STR1,"");
    break;
  case UNKNOWN:
    Undefined(Name);
  default:
    Abort("Unsupported Store mechanism");
  }
  trace ('-',"Store",Name,"",0,0);
}


/*---------------------------------------------------------------*/
/* Push Primary onto Stack */

static void Push()
{
  Stage (PC_PUSH_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* Push Secondary onto Stack */

static void PushSec()
{
  Stage (PC_PUSH_SECONDARY,"","","");
}


/*---------------------------------------------------------------*/
/* Move Primary to Secondary */

static void MovePrimSec()
{
  Stage (PC_PRIMARY_SECONDARY,"","","");

}


/*---------------------------------------------------------------*/
/* Add Top of Stack to Primary */

static void PopAdd()
{
  Stage (PC_POPADD_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* Subtract Primary from Top of Stack */

static void PopSub()
{
  Stage (PC_POPSUB_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* Multiply Top of Stack by Primary */

static void PopMul()
{
  Stage (PC_POPMUL_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* Divide Top of Stack by Primary */

static void PopDiv()
{
  Stage (PC_POPDIV_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* AND Top of Stack with Primary */

static void PopAnd()
{
  Stage (PC_POPAND_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* OR Top of Stack with Primary */

static void PopOr()
{
  Stage (PC_POPOR_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* XOR Top of Stack with Primary */

static void PopXor()
{
  Stage (PC_POPXOR_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* Compare Top of Stack with Primary */

static void PopCompare()
{
  Stage (PC_POPCMP_PRIMARY,"","","");
}


/*---------------------------------------------------------------*/
/* Set primary If Compare was = */

static void SetEqual()
{
  Stage (PC_SET_PRIMARY_EQ,"","","");
}


/*---------------------------------------------------------------*/
/* Set primary If Compare was != */

static void SetNEqual()
{
  Stage (PC_SET_PRIMARY_NE,"","","");
}


/*---------------------------------------------------------------*/
/* Set primary If Compare was > */

static void SetGreater()
{
  Stage (PC_SET_PRIMARY_GT,"","","");
}


/*---------------------------------------------------------------*/
/* Set primary If Compare was < */

static void SetLess()
{
  Stage (PC_SET_PRIMARY_LT,"","","");
}


/*---------------------------------------------------------------*/
/* Set primary If Compare was <= */

static void SetLessOrEqual()
{
  Stage (PC_SET_PRIMARY_LE,"","","");
}


/*---------------------------------------------------------------*/
/* Set primary If Compare was >= */

static void SetGreaterOrEqual()
{
  Stage (PC_SET_PRIMARY_GE,"","","");
}


/*---------------------------------------------------------------*/
/* Branch Unconditional  */

static void Branch(char *L)
{
  Stage (PC_BRANCH,L,"","");
}


/*---------------------------------------------------------------*/
/* Branch False */

static void BranchFalse(char *L)
{
  Stage (PC_BRANCH_FALSE,L,"","");
}


/*--------------------------------------------------------------*/
/* Write Header Info */

static void Header()
{
  Stage (PC_HEADER,"","","");
}


/*--------------------------------------------------------------*/
/* Write the Prolog */

static void Prolog()
{
  Stage (PC_PROLOG,"","","");
}


/*--------------------------------------------------------------*/
/* Write the Epilog */

static void Epilog()
{
  char STR1[STRTMPSIZE];

  sprintf (STR1,"%d",TotLocSize);
  if (TotLocSize) Stage (PC_SHRINKSTACK,STR1,"","");
  Stage (PC_EPILOG,"","","");
}


/*--------------------------------------------------------------*/
/* Allocate Storage for a static Variable */

static void Allocate(char *Name, char *Val)
{
  Stage (PC_ALLOCATE,Name,Val,"");
}


/*--------------------------------------------------------------*/
/* Move address of var to Primary */

static void LoadAddrVar (char *varname)
{
  switch (WhatIs(varname)) {
  case UNKNOWN:
    Undefined(varname);
  case GLOBAL:
    Stage (PC_MOVE_A_PRIMARY,varname,"","");
    break;
  case LOCAL:
    Stage (PC_MOVE_AL_PRIMARY,varname,"","");
    break;
  }
}


/*--------------------------------------------------------------*/
/* Pop to Secondary */

static void PopSec()
{
    Stage (PC_POP_SECONDARY,"","","");
}


/*--------------------------------------------------------------*/
/* Adjust Secondary according to variable's data type size */

static void AdjustSec(char *varname)
{
  int dsize;
  char STR1[STRTMPSIZE];

  switch (WhatIs(varname)) {
  case UNKNOWN:
    Undefined(varname);
  case GLOBAL:
    dsize = SizeOfType(GS_Type[Locate(varname)-1]);
    break;
  case LOCAL:
    dsize = SizeOfType(LS_Type[IsLocal(varname)-1]);
    break;
  }
  if (dsize != 1) {
    sprintf(STR1,"%d",dsize);
    Stage (PC_ADJUST_SECONDARY,varname,STR1,"");
  }
}


/*--------------------------------------------------------------*/
/* Add Primary to Secondary */

static void AddPrimSec()
{
    Stage (PC_ADD_PRIMARY_SEC,"","","");
}


/*--------------------------------------------------------------*/
/* Move [Secondary] to Primary */

static void MoveSecIPrim()
{
    Stage (PC_MOVE_SI_PRIMARY,"","","");
}


/*--------------------------------------------------------------*/
/* Move Primary to [Secondary] */

static void MovePrimSecI()
{
    Stage (PC_MOVE_PRIMARY_SI,"","","");
}


/*--------------------------------------------------------------*/
/* Parse and Translate a Math Factor */

static void Factor()
{
  char SavedValue[STRTMPSIZE], SavedToken;

  trace ('+',"Factor",Value,"",0,0);
  if (Token == '(') {
    Next();
    BoolExpression();
    MatchString(")");
    trace ('-',"Factor","","",0,0);
    return;
  }
  strcpy(SavedValue,Value);
  SavedToken=Token;
  Next();

  if (SavedToken == TOK_IDENTIFIER) {
    if (Token == '[') {			// an array element...
      Next();
      BoolExpression();			// evaluate element
      MovePrimSec();			// save element to secondary
      MatchString("]");
      LoadAddrVar(SavedValue);		// load address of array to Primary
      AdjustSec(SavedValue);		// adjust secondary by array's data type
      AddPrimSec();			// add primary to secondary
      MoveSecIPrim();			// move [secondary] to primary
      trace ('-',"Factor[]","","",0,0);
      return;
    }
    else
      LoadVar(SavedValue);
  }
  else if (SavedToken == TOK_NUM)
    LoadConst(SavedValue);
  else
    Expected("Math Factor");
  trace ('-',"Factor.","","",0,0);
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Multiply */

static void Multiply()
{
  Next();
  Factor();
  PopMul();
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Divide */

static void Divide()
{
  Next();
  Factor();
  PopDiv();
}


/*---------------------------------------------------------------*/
/* Parse and Translate a Math Term */

static void Term()
{
  trace ('+',"Term","","",0,0);
  Factor();
  while (IsMulop(Token)) {
    Push();
    switch (Token) {
    case '*':
      Multiply();
      break;
    case '/':
      Divide();
      break;
    }
  }
  trace ('-',"Term","","",0,0);
}


/*--------------------------------------------------------------*/
/* Recognize and Translate an Add */

static void Add()
{
  Next();
  Term();
  PopAdd();
}


/*-------------------------------------------------------------*/
/* Recognize and Translate a Subtract */

static void Subtract()
{
  Next();
  Term();
  PopSub();
}


/*---------------------------------------------------------------*/
/* Parse and Translate an Expression */

static void Expression()
{
  trace ('+',"Expression","","",0,0);
  if (IsAddop(Token))
    Clear();
  else
    Term();
  while (IsAddop(Token)) {
    Push();
    switch (Token) {
    case '+':
      Add();
      break;
    case '-':
      Subtract();
      break;
    }
  }
  trace ('-',"Expression","","",0,0);
}


/*---------------------------------------------------------------*/
/* Get Another Expression and Compare */

static void CompareExpression()
{
  Expression();
  PopCompare();
}


/*---------------------------------------------------------------*/
/* Get The Next Expression and Compare */

static void NextExpression()
{
  Next();
  CompareExpression();
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Relational "Equals" */

static void Equal()
{
  NextExpression();
  SetEqual();
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Relational "Less Than or Equal" */

static void LessOrEqual()
{
  NextExpression();
  SetLessOrEqual();
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Relational "Not Equals" */

static void NotEqual()
{
  NextExpression();
  SetNEqual();
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Relational "Less Than" */

static void Less()
{
  Next();
  switch (Token) {
  case '=':
    LessOrEqual();
    break;
  case '>':
    NotEqual();
    break;
  default:
    CompareExpression();
    SetLess();
    break;
  }
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Relational "Greater Than" */

static void Greater()
{
  Next();
  if (Token == '=') {
    NextExpression();
    SetGreaterOrEqual();
  } else {
    CompareExpression();
    SetGreater();
  }
}


/*---------------------------------------------------------------*/
/* Parse and Translate a Relation */


static void Relation()
{
  Expression();
  if (!IsRelop(Token)) return;
  Push();
  switch (Token) {
  case '=':
    Equal();
    break;
  case '<':
    Less();
    break;
  case '>':
    Greater();
    break;
  }
}


/*---------------------------------------------------------------*/
/* Parse and Translate a int Factor with Leading NOT */

static void NotFactor()
{
  trace ('+',"NotFactor","","",0,0);
  if (Token != '!') {
    Relation();
    trace ('-',"NotFactor","","",0,0);
    return;
  }
  Next();
  Relation();
  NotIt();
  trace ('-',"NotFactor.","","",0,0);
}


/*---------------------------------------------------------------*/
/* Parse and Translate a int Term */

static void BoolTerm()
{
  trace ('+',"BoolTerm","","",0,0);
  NotFactor();
  while (Token == '&') {
    Push();
    Next();
    NotFactor();
    PopAnd();
  }
  trace ('-',"BoolTerm","","",0,0);
}


/*--------------------------------------------------------------*/
/* Recognize and Translate a int OR */

static void BoolOr()
{
  Next();
  BoolTerm();
  PopOr();
}


/*--------------------------------------------------------------*/
/* Recognize and Translate an Exclusive Or */

static void BoolXor()
{
  Next();
  BoolTerm();
  PopXor();
}


/*---------------------------------------------------------------*/
/* Parse and Translate a int Expression */

static void BoolExpression()
{
  trace ('+',"BoolExpression","","",0,0);
  BoolTerm();
  while (IsOrop(Token)) {
    Push();
    switch (Token) {
    case '|':
      BoolOr();
      break;
    case '~':
      BoolXor();
      break;
    }
  }
  trace ('-',"BoolExpression","","",0,0);
}


/*--------------------------------------------------------------*/
/* Parse and Translate an Assignment Statement */

static void Assignment()
{
  char Name[STRTMPSIZE];

  trace ('+',"Assignment",Value,"",0,0);
  if (!InTable(Value) && !IsLocal(Value)) Undefined(Value);
  strcpy(Name, Value);
  Next();

  if (Token == '[') {		// an array element...
    Next();
    BoolExpression();		// evaluate element
    MovePrimSec();		// save element to secondary
    MatchString("]");		
    LoadAddrVar(Name);		// load address of array to primary
    AdjustSec(Name);		// adjust element by array's data type
    AddPrimSec();		// add primary to secondary
    MatchString("=");
    PushSec();			// push LHS (an address)
    BoolExpression();		// process RHS
    PopSec();			// pop LHS (an address) to secondary
    MovePrimSecI();		// move primary to [secondary]
    }
  else {
    MatchString("=");
    BoolExpression();
    Store(Name);
  }
  trace ('-',"Assignment","","",0,0);
}


/*---------------------------------------------------------------*/
/* Recognize and Translate an IF Construct */

static void DoIf()
{
  char L1[STRTMPSIZE], L2[STRTMPSIZE];

  trace ('+',"DoIf","","",0,0);
  Next();
  BoolExpression();
  NewLabel(L1);
  strcpy(L2, L1);
  BranchFalse(L1);
  Block();
  if (Token == TOK_ELSE) {
    Next();
    NewLabel(L2);
    Branch(L2);
    PostLabel(L1);
    Block();
  }
  PostLabel(L2);
  MatchString("endif");
  trace ('-',"DoIf","","",0,0);
}


/*--------------------------------------------------------------*/
/* Parse and Translate a WHILE Statement */

static void DoWhile()
{
  char L1[STRTMPSIZE], L2[STRTMPSIZE];

  trace ('+',"DoWhile","","",0,0);
  Next();
  NewLabel(L1);
  NewLabel(L2);
  PostLabel(L1);
  BoolExpression();
  BranchFalse(L2);
  Block();
  MatchString("endwhile");
  Branch(L1);
  PostLabel(L2);
  trace ('-',"DoWhile","","",0,0);
}


/*--------------------------------------------------------------*/
/* Parse and Translate a Block of Statements */

static void Block()
{
  trace ('+',"Block","","",0,0);
  Scan();
  while (Token != TOK_ELSE && Token != TOK_END) {
    switch (Token) {
    case TOK_IF:
      DoIf();
      break;
    case TOK_WHILE:
      DoWhile();
      break;
    default:
      Assignment();
      break;
    }
    Scan();
  }
  trace ('-',"Block","","",0,0);
}


/*--------------------------------------------------------------*/
/* Calculate array size of a declaration */

static int ArraySize()
{
  int  asize = 1;

  if (Token == '[') {
    Next();
    Scan();
    if (Token != TOK_NUM) Abort ("Number Expected");
    asize = atoi(Value);
    Next();
    MatchString("]");
  }
  return asize;
}


/*--------------------------------------------------------------*/
/* Allocate Storage for a Variable */

static void Alloc(char datatype)
{
  char STR1[STRTMPSIZE];
  char name[MAXSYMSIZE+1];
  int  asize;

  trace ('+',"Alloc","","",0,0);
  Next();
  if (Token != TOK_IDENTIFIER) Expected("Variable Name");
  strcpy (name,Value);
  CheckDup(name);
  AddEntry(name, datatype);
  Next();
  asize = ArraySize();
  sprintf(STR1,"%d",SizeOfType(datatype)*asize);
  GS_Size[NumGlobals-1] = asize;
  Allocate(name, STR1);

  trace ('-',"Alloc","","",0,0);
}


/*--------------------------------------------------------------*/
/* Parse and Translate Global Declarations */

static void TopDecls()
{
  char datatoken;

  linenumber = 1;
  trace ('+',"TopDecls","","",0,0);
  Scan();
  while (Token == TOK_CHARDEF || Token == TOK_INTDEF) {
    datatoken = Token;
    Alloc(datatoken);
    while (Token == ',') Alloc(datatoken);
    Scan();
  }
  trace ('-',"TopDecls","","",0,0);
}


/*--------------------------------------------------------------*/
/* Add a Local to Table */

static void AddLocal (char *name, char ltype)
{
  trace ('+',"AddLocal","","",0,0);
  if (IsLocal(name)) Duplicate(name);
  strcpy(LS_Name[NumLocals],name);
  LS_Type[NumLocals]=ltype;
  NumLocals++;
  trace ('-',"AddLocal","","",0,0);
}


/*--------------------------------------------------------------*/
/* Allocate Storage for a Local Variable */

static void AllocLocal(char vtype)
{
  char name[STRTMPSIZE];
  int  asize;

  trace ('+',"AllocLocal","","",0,0);
  Next();
  if (Token != TOK_IDENTIFIER) Expected("Variable Name");
  AddLocal(Value, vtype);
  strcpy(name,Value);
  Next();
  asize = ArraySize();
  LS_Size[NumLocals-1] = asize;
  TotLocSize += (asize * SizeOfType(vtype));
  trace ('-',"AllocLocal",name,"",0,0);
}


/*--------------------------------------------------------------*/
/* Initialize Locals Table to Null */

static void ClearLocals()
{
  int i;
  trace ('+',"ClearLocals","","",0,0);
  for (i=0; i<MAXLOCALS; i++)
    { *LS_Name[i] = 0; LS_Size[i] = 0; LS_Type[i] = 0;}
  NumLocals = 0;
  trace ('-',"ClearLocals","","",0,0);
  TotLocSize = 0;
}


/*--------------------------------------------------------------*/
/* Process Local Variable Declarations */

static void DoLocals()
{
  char STR1[STRTMPSIZE], datatoken;

  trace ('+',"DoLocals","","",0,0);
  ClearLocals();
  Scan();
  if (Token == TOK_CHARDEF || Token == TOK_INTDEF) {
    while (Token == TOK_CHARDEF || Token == TOK_INTDEF) {
      datatoken = Token;
      AllocLocal(datatoken);
      while (Token == ',') AllocLocal(datatoken);
      Scan();
    }
    sprintf (STR1,"%d",TotLocSize);
    Stage (PC_GROWSTACK,STR1,"","");
  }
  trace ('-',"DoLocals","","",0,0);
}


/*--------------------------------------------------------------*/
/* Initialize */

static void Init()
{
  trace ('+',"Init","","",0,0);
  Getchar();
  Next();
  trace ('-',"Init","","",0,0);
}


/*--------------------------------------------------------------*/
/* Main Program */

main(int argc, char *argv[])
{
  printf ("\n"); printf (COMPILER);
  if (argc < 2) {
    interactive = TRUE;
    EmitLn_notab ("Interactive Mode");
  }
  else {
    if ((ip=fopen(argv[1],"r"))==NULL) Abort ("File not found");
  }
  if (tracing) {
    if ((tr=fopen("trace.txt","w"))==NULL) Abort ("Can not open trace file");
    fprintf (tr,COMPILER);
  }
  if (opfile1) {
    if ((op1=fopen("out1.txt","w"))==NULL) Abort ("Can not open output file 1");
    fprintf (op1,"; "); fprintf (op1,COMPILER);
    op = op1;
  }
  if (opfile2) {
    if ((op2=fopen("out2.txt","w"))==NULL) Abort ("Can not open output file 2");
    fprintf (op2,"; "); fprintf (op2,COMPILER);
  }

  Init();
  Header();
  TopDecls();
  MatchString("program");
  MatchString("begin");
  Prolog();
  DoLocals();
  Block();
  MatchString("end");
  Epilog();
  dump_symbol();	// dump the symbol tables to the trace file
  dump_pcodes();	// dump the unoptimized pcodes to the trace file
  Gen_Code(op1);	// generate output from unoptimized pcodes to file 1
  if (opfile2) {
    Optimize();
    dump_pcodes();
    Gen_Code(op2);	// generate output from optimized pcodes to file 2
  }
  printf ("\n%d symbols used\n%d pcodes generated\n",NumGlobals,sbufftop);
  printf ("End of Compilation\n");

  if (tracing) fclose (tr);
  if (opfile1) fclose (op1);
  if (opfile2) fclose (op2);
  exit(EXIT_SUCCESS);
}
/*--------------------------------------------------------------*/

