/*******************************************************************
   Program :  Recursive Descent Compiler for PL/0
   Module  :  PL0PARS - Parser: syntax analysis, semantic
                                analysis, intermediate code
                                generation
   File    :  pl0pars.c
   Compiler:  Borland C 3.1 - 4.5, GNU C 2.7.1
   Author  :  H. Weber
   Revision:  Aug. 1998
********************************************************************/

#include <string.h>
#include <stdio.h>
#include "pl0const.h"
#include "pl0glob.h"
#include "pl0symt.h"
#include "pl0err.h"
#include "pl0code.h"              /* cg */

#define maxline 100

FILE *token, *listing,
     *errlist, *stlist;
FILE *intcode;                    /* cg */
int  addr, level;                 /* cg */
int  i, sym, num,
     txcurr, levcurr;
char id[cmax];
char str[strmax];
char lline[maxline];

void statement();
void expression();
void block();


void nextline()
{
   if (fgets(lline, maxline-2, listing) == NULL) {
      fprintf(stderr, "Cannot read from listing file\n");
      exit(1);
   }
   fputs(lline, errlist);
}

void getsym()
/* read next symbol (token) from token file */
{
   int i;

   i = fscanf(token, "%d", &sym);
   if (i <= 0) {
      fprintf(stderr, "Error reading token file\n");
      exit(1);
   }
   if (sym == lf) { nextline(); getsym(); }
   else if (sym == ident) fscanf(token, "%s", id);
   else if (sym == number) fscanf(token, "%d", &num);
   else if (sym == stringsym) {
     fgets(str, 3, token);
     fgets(str, strmax, token);
     str[strlen(str)-1] = 0;
   }
}

void match(int symb, int errno)
/* check current symbol and get next symbol */
{
   if (sym == symb)
      getsym();
   else
      error(errno);
}

/* syntactic functions follow */

void constdecl(int *tx, int *txfirst, int *dindex)
{
   match(ident, 4);
   match(eql, 3);
   if (sym == number) {
      enter(constobj, id, 0, num, tx,
            txfirst, dindex);
      getsym();
   } else error(2);
}

void vardecl(int *tx, int *txfirst, int *dindex, int lev)
{
   int i, arrsize;
   char arrid[cmax];

   match(ident, 4);
   enter(varobj, id, lev, 0, tx, txfirst, dindex);
}

void constpart(int *tx, int *txfirst, int *dindex)
{
   getsym();
   constdecl(tx, txfirst, dindex);
   while (sym == comma) {
      getsym();
      constdecl(tx, txfirst, dindex);
   }
   match(semicolon, 5);
}

void varpart(int *tx, int *txfirst, int *dindex, int lev)
{
   getsym();
   vardecl(tx, txfirst, dindex, lev);
   while (sym == comma) {
      getsym();
      vardecl(tx, txfirst, dindex, lev);
   }
   match(semicolon, 5);
}

void procpart(int *tx, int *txfirst, int *dindex, int lev)
{
   getsym();
   if (sym == ident) {
      enter(procobj, id, lev, 0, tx,
            txfirst, dindex);
      getsym();
   } else error(4);
   match(semicolon, 5);
   block(*tx, lev+1);
   match(semicolon, 5);
}

void factor()
{
   int i, k;

   if (sym == ident) {
      i = position(id, txcurr);
      if (i == 0) error(11);
      k = objkind(i);
      switch (k) {
         case procobj:
            error(21);
            break;
         case constobj:
	    emit(LIT, 0, constsize(i));              /* cg */
	    break;
	 case varobj:
	    emit(LOD, levcurr-objlevel(i), objaddr(i));
						     /* cg */
	    break;
      } /* switch */
      getsym();
   }
   else if (sym == number) {
      emit(LIT, 0, num);                             /* cg */
      getsym();
   }
   else if (sym == lparen) {
      getsym();
      expression();
      match(rparen, 22);
   } else error(23);
}

void term()
{
   int symbol;                                       /* cg */

   factor();
   while (sym == times || sym == slash) {
      symbol = sym;                                  /* cg */
      getsym();
      factor();
      if (symbol == times) emit(MPY, 0, 0);          /* cg */
      else emit(DVD, 0, 0);                          /* cg */
   }
}

void expression()
{
   int symbol;                                       /* cg */

   if (sym == plus || sym == minus) {
      symbol = sym;                                  /* cg */
      getsym();
      term();
      if (symbol == minus) emit(NEG, 0, 0);          /* cg */
   } else term();
   while (sym == plus || sym == minus) {
      symbol = sym;                                  /* cg */
      getsym();
      term();
      if (symbol == plus) emit(ADD, 0, 0);           /* cg */
      else emit(SUB, 0, 0);                          /* cg */
   }
}

void condition()
{
   if (sym == oddsym) {
      getsym();
      expression();
      emit(ODD, 0, 0);                               /* cg */
   } else {
      expression();
      if (sym == eql) {
         getsym();
         expression();
	 emit(EQL, 0, 0);                            /* cg */
      } else if (sym == neq) {
         getsym();
         expression();
	 emit(NEQ, 0, 0);                            /* cg */
      } else if (sym == lss) {
         getsym();
         expression();
	 emit(LSS, 0, 0);                            /* cg */
      } else if (sym == geq) {
         getsym();
         expression();
	 emit(GEQ, 0, 0);                            /* cg */
      } else if (sym == gtr) {
         getsym();
         expression();
	 emit(GTR, 0, 0);                            /* cg */
      } else if (sym == leq) {
         getsym();
         expression();
	 emit(LEQ, 0, 0);                            /* cg */
      } else error(20);
   }
}

void assignstmnt()
{
   int i, k;

   i = position(id, txcurr);
   if (i == 0) error(11);
   k = objkind(i);
   if (k == constobj || k == procobj) error(12);
   if (k == varobj) {
      getsym();
      match(becomes, 13);
      expression();
      emit(STO, levcurr-objlevel(i), objaddr(i));    /* cg */
   }
}

void callstmnt()
{
   int i;

   getsym();
   if (sym == ident) {
      i = position(id, txcurr);
      if (i == 0) error(11);
      if (objkind(i) != procobj) error(15);
      emit(CAL, levcurr-objlevel(i), objaddr(i));    /* cg */
      getsym();
   }  else error(14);
}

void ifstmnt()
{
   int fixup1, fixup2;                               /* cg */

   getsym();
   condition();
   match(thensym, 16);
   fixup1 = cdindex();                               /* cg */
   emit(JPC, 0, 0);                                  /* cg */
   statement();
   if (sym == elsesym) {
      getsym();
      fixup2 = cdindex();                            /* cg */
      emit(JPU, 0, 0);                               /* cg */
      fixup(fixup1, cdindex());                      /* cg */
      statement();
      fixup(fixup2, cdindex());                      /* cg */
   } else {
      fixup(fixup1, cdindex());                      /* cg */
   }
}

void compoundstmnt()
{
   getsym();
   statement();
   while (sym == semicolon) {
      getsym();
      statement();
   }
   match(endsym, 17);
}

void whilestmnt()
{
   int fixup1, fixup2;                               /* cg */

   fixup1 = cdindex();                               /* cg */
   getsym();
   condition();
   fixup2 = cdindex();                               /* cg */
   emit(JPC, 0, 0);                                  /* cg */
   match(dosym, 18);
   statement();
   emit(JPU, 0, fixup1);                             /* cg */
   fixup(fixup2, cdindex());                         /* cg */
}

void readstmnt()
{
   int i, k;

   getsym();
   if (sym == lparen) {
      do {
         getsym();
         if (sym == ident) {
            i = position(id, txcurr);
            if (i == 0) error(11);
            k = objkind(i);
            if (k == constobj || k == procobj)
               error(12);
            else if (k == varobj) {
	       emit(INP, 0, 0);                      /* cg */
	       emit(STO, levcurr-objlevel(i), objaddr(i));
						     /* cg */
	       getsym();
	    }
	 } else error(33);
      } while (sym == comma);
      match(rparen, 22);
   } else error(43);
}

void writestmnt()
{
   int i;
   int l;                                            /* cg */

   getsym();
   if (sym == lparen) {
      do {
         getsym();
         if (sym == stringsym) {
	    l = strlen(str);                         /* cg */
	    emit(PRS, 0, l);                         /* cg */
	    for (i=0; i<l; i++) {                    /* cg */
	       emit(ASC, 0, (int) str[i]);           /* cg */
	    }                                        /* cg */
	    getsym();
         }
         else {
            expression();
	    emit(PRN, 0, 0);                         /* cg */
	 }
      } while (sym == comma);
      match(rparen, 22);
   } else error(43);
}

void writelnstmnt()
{
   getsym();
   emit(PRL, 0, 0);                                  /* cg */
}

void statement()
{
   if      (sym == ident)      assignstmnt();
   else if (sym == callsym)    callstmnt();
   else if (sym == ifsym)      ifstmnt();
   else if (sym == beginsym)   compoundstmnt();
   else if (sym == whilesym)   whilestmnt();
   else if (sym == readsym)    readstmnt();
   else if (sym == writesym)   writestmnt();
   else if (sym == writelnsym) writelnstmnt();
}

void block(int tx, int lev)
{
   int ppos;     /* procedure position in
		    symbol table */
   int dindex;   /* data allocation index  */
   int txfirst;  /* index of first declaration
		    in block */
   int oldcdi;   /* saves old code index  */         /* cg */
   int currcdi;  /* current code index   */          /* cg */

   txfirst = tx + 1;
   ppos = tx;
   dindex = 3;

   oldcdi = cdindex();                               /* cg */
   if (lev == 0) emit(CAL, 0, 0);                    /* cg */

   if (sym == constsym)
      constpart(&tx, &txfirst, &dindex);
   if (sym == varsym)
      varpart(&tx, &txfirst, &dindex, lev);
   while (sym == procsym)
      procpart(&tx, &txfirst, &dindex, lev);

   currcdi = cdindex();                              /* cg */
   if (lev == 0) fixup(oldcdi, currcdi);             /* cg */
   entersymt(ppos, currcdi, dindex);                 /* cg */
   emit(INT, 0, dindex-3);                           /* cg */

   txcurr = tx;
   levcurr = lev;
   statement();

   emit(RET, 0, 0);                                  /* cg */

   printsymt(stlist, ppos, tx);
}

void program()
{
   block(0, 0);
   if (sym != period) error(9);
}

main(int argc, char *argv[])
/*------------------------------------------------------------------
   The main function of the parser opens and closes files and
   calls the function "program"
------------------------------------------------------------------*/
{
   int i;
   char tname[20], lname[20],
        ename[20], sname[20];
   char cname[20];                                   /* cg */
   char *p;

   /* check arguments */
   if (argc == 1) {
      fprintf(stderr, "Usage: %s source\n",
	      argv[0]);
      exit(1);
   }

   /* set up filenames */
   strcpy(tname, argv[1]);
   p = strchr(tname, '.');
   if (p) *p = 0;
   strcpy(lname, tname);
   strcpy(ename, tname);
   strcpy(sname, tname);
   strcpy(cname, tname);                             /* cg */
   strcat(tname, ".tok");
   strcat(lname, ".lst");
   strcat(ename, ".err");
   strcat(sname, ".stb");

   printf("PL/0-Compiler: Parser\n");
   /* open files */
   if ((token = fopen(tname, "r")) == NULL) {
      fprintf(stderr, "Cannot open token file %s\n",
              tname);
      exit(1);
   }
   if ((listing = fopen(lname, "r")) == NULL) {
      fprintf(stderr, "Cannot open listing file %s\n",
              lname);
      exit(1);
   }
   if ((errlist = fopen(ename, "w")) == NULL) {
      fprintf(stderr,
	      "Cannot open error listing file %s\n",
              ename);
      exit(1);
   }
   if ((stlist = fopen(sname, "w")) == NULL) {
      fprintf(stderr,
	      "Cannot open symbol table listing file %s\n",
              sname);
      exit(1);
   }

   /* read first 3 lines of listing and write */
   for (i=0; i<3; i++) {
      if (fgets(lline, maxline-2, listing) == NULL) {
         fprintf(stderr, "Cannot read from listing file\n");
         exit(1);
      }
      fputs(lline, errlist);
   }

   /* get first symbol and start recursive descent */
   getsym();
   program();

   /* if we are here, the input is ok. */
   /* write code array on code file */               /* cg */
   strcat(cname, ".cod");                            /* cg */
   if ((intcode = fopen(cname, "w")) == NULL) {      /* cg */
      fprintf(stderr,
	   "Cannot open intermediate code file %s\n",
	   cname);                                   /* cg */
      exit(1);                                       /* cg */
   }                                                 /* cg */
   writecode();                                      /* cg */
   fclose(intcode);                                  /* cg */

   /* close files */
   fclose(token);
   fclose(listing);
   fclose(errlist);
   fclose(stlist);

   /* erase listing file */
   unlink(lname);
   return;
}
