/* Further modified, 20 Feb 2002, parser only
 This works
 This compiler supports only lower-case chracters
 sign != given by #
 sign <= given by $
 sign >= given by %
*/
#include <stdio.h>
struct namerecord{
  int kind;
  char name[10];
};
int i,ii,j,k,m, n,kind;
char line[81];
int  norw=11;     /* no. of reserved words */
int  txmax=100;   /* length of indentifier table */
int  nmax=14;     /* max. no. of digits in numbers */
int  al=10;       /* length of identifier */ 
int  amax=2047;   /* maximum address */
/* The following is for symbol type */
int nul=1; int ident=2; int number=3; int plus=4 ;
int minus=5; int mult=6; int slash=7; int oddsym=8;
int eql=9; int neq=10; int lss=11; int leq=12;
int gtr=13; int geq=14; int lparen=15; int rparen=16; 
int comma=17; int semicolon=18; int period=19; int becomes=20; 
int beginsym=21; int endsym=22; int ifsym=23; int thensym=24; 
int whilesym=25; int dosym=26; int callsym=27; int constsym=28; 
int varsym=29; int procsym=30;
int constant=1; int variable=2; int procedure=3;
int null=0;

char ch,ch1,dummy; /*last character read */
int  sym ;    /* last symbol read */
char *id;  /* last identifier read */
int num;   /* last number read */
int cc;   /* character count */
int ll;   /* line length */
int kk,ii;
char a[10];
struct namerecord table[1000];
char *word[100];
int wsym[1000];
int ssym[1000];
char *mnemonic[100];
FILE *f;
char c;
void error(i)
 int i;
{ printf("error %d\n",i); }
getline()
{
 cc=0; ll=0; n=0;
 while (((c=getc(f))!=10))
 {
  if (c==EOF){ch=-1; return 0;}
  n++; ll++;
  line[n]=c;
 }
 for (i=1; i<=n; i++)printf("%c", line[i]);
 printf("\n");
 return 1;
}
getch()  /* get one character */
{
  if (cc==ll){
   if (ch==-1) {printf("PROGRAM INCOMPLETE"); getchar(); return 0;}
   getline();
  }
  cc=cc+1;
  ch=line[cc];
}
search()
{ int i, j, len;
  char *s;
  for (i=norw; i>=1; i--) {
    s=word[i];
    len=strlen(s);
    for (j=1; j<=len; j++){
      if(a[j]!=*s) break; 
      s++;
    }
    if (j>len){ return i;}
  }
  return 0;
}
getsym() /* gets one symbol */
{
  while (ch==' ') getch();
  if ((ch >= 'A') && (ch <= 'z')) {
    k=0;
    do{  
      if (k<al){k++; a[k]=ch;};
      getch(); 
    } while(((ch>='0') && (ch<='9')) || ((ch>='A') && (ch<='z')));
   a[k+1]=0;
   id=a+1;  
   if ((i=search())!=0)sym=wsym[i]; else sym=ident; 
  } else
  if ((ch>='0') && (ch <= '9')) {
    k=0; num=0; sym=number;
      do { num=10*num + (ch - '0');
           k++; getch();
      }
      while ((ch >= '0') && (ch <= '9'));
      if (k > nmax) error(30);
  } else
  if (ch==':') {
    getch();
    if (ch=='='){
      sym=becomes; getch();
    } else sym=null;
  } else if (ch==-1) {return;}
  else
  {
    sym=ssym[ch]; getch();
  } 
}
enter(k,ptx) /* This enters a symbol into the table */
int k; int *ptx;
{
  char *id1;
  int ii,len;
  (*ptx)++; 
  id1=id;
  len=strlen(id);
  for (ii=0;ii<=len;ii++) {table[*ptx].name[ii]=*id1; id1++;}
  table[*ptx].kind=k;
}
mismatch(a,b)
char *a; char *b;
{
  int i; int len1; int len2;
  len1=strlen(a); len2=strlen(b);
    i=0;
  if (len1!=len2) return 1; else
  {
    while(i<=len2-1) { 
      if (a[i]!=b[i]) return 1;
      i++; /* a++; */
    }
  return 0;
  }
}
int position(id,ptx) /* This finds the position of id */
char *id;
int *ptx;
{
 char buf[10];
 char *id1;
 int i,ii,len;
 id1=id;
 len=strlen(id);
  for (i=0;i<=len;i++) {table[0].name[i]=*id1; id1++;} 
  i=*ptx;
  do {
    len=strlen(table[i].name);
    for (ii=0;ii<=len-1;ii++) {
      buf[ii]=table[i].name[ii]; 
    }
    buf[ii]=0;
    i--;}
  while (mismatch(buf, id)); 
  return i+1;
}
constdeclaration(ptx)
int *ptx;
{
  if (sym==ident) {
    getsym();
    if ((sym==eql) || (sym==becomes)) {
      if (sym==becomes) error(1);
      getsym();
      if (sym==number) {enter(constant,ptx); getsym();}
    }
  }
}
vardeclaration(ptx)
int *ptx;
{
  if (sym==ident) {enter(variable, ptx); getsym();}
  else error(4); 
}
factor(ptx)
int *ptx;
{
  int i;
    if (sym==ident) 
    {
      i=position(id,ptx);
      if (i==0) error(11); 
      else
      {
        kind=table[i].kind;
        if (kind==procedure) error(21); 
        getsym();
      }
    }
    else if(sym==number) getsym();
    else if(sym==lparen) 
    { getsym(); expression(ptx); 
        if (sym==rparen)getsym(); else error(22);
    }
}
term(ptx)
int *ptx;
{
  factor(ptx);
  while((sym==mult)||(sym==slash)) {
    getsym(); factor(ptx);
  }
}
expression(ptx)
int *ptx;
{
  if ((sym==plus) ||(sym==minus)){
     getsym();}
  term(ptx);
  while ((sym==plus)||(sym==minus)) {
    getsym(); term(ptx);
  }
}
condition(ptx)
int *ptx;
{
  if(sym==oddsym) {
    getsym(); expression(ptx); }
    else {
      expression(ptx);
      if ((sym!=eql)&&(sym!=neq)&&(sym!=lss)&&
         (sym!=leq)&&(sym!=gtr)&&(sym!=geq)) error(20);
      else {
        getsym(); expression(ptx);
      }
    }
}
statement(ptx)
int *ptx;
{
  int i;
  if (sym==ident){
    i=position(id,ptx);
    if(i==0) error(11); else
    if (table[i].kind!=variable) {
      error(12); i=0;
    };
    getsym(); 
    if (sym==becomes) getsym(); else error(13);
    expression(ptx);
  } else
  if (sym==callsym) { /* procedure call */
    getsym();
    if (sym!=ident) error(14); else
    { i=position(id, ptx); 
      if(i==0) error(11); else
      if (table[i].kind!=procedure) error(15); 
      getsym();
    }
  } else
  if (sym==ifsym) { /* if statement */
     getsym(); condition(ptx);
     if (sym==thensym) getsym(); else error(16);
     statement(ptx); 
  } else
  if (sym==beginsym) {  /* begin ... end */
    getsym();
    statement(ptx);
    while(sym==semicolon)
    { getsym(); statement(ptx);}
    if (sym==endsym) getsym(); else error(17); 
  } else
  if (sym==whilesym) { /* while statement */
    getsym(); condition(ptx);
    if (sym==dosym) getsym(); else error(18);
    statement(ptx); 
  }
}
block(tx)
int tx;
{
  int tx0;
  tx0=tx;  
  do {
    if (sym==constsym) {
      getsym();
      do {
        constdeclaration(&tx);
        while(sym==comma) {
          getsym(); constdeclaration(&tx);
        }
        if(sym==semicolon)getsym(); else error(5);
      } while (sym==ident);
    }
    if (sym==varsym) {
      getsym();
      do { vardeclaration(&tx);
        while (sym==comma) {
          getsym(); vardeclaration(&tx);
        }
      if(sym==semicolon) getsym(); else error(5);
      } while(sym==ident);
    }
    while(sym==procsym) {
      getsym();
      if(sym==ident){
        enter(procedure,&tx); getsym();
      } else error(4);
      if (sym==semicolon) getsym(); else error(5);
      block(tx);
      if(sym==semicolon) {
        getsym();
      } else error(5);
    }
  }while ((sym==constsym)||(sym==varsym)||(sym==procsym));
  statement(&tx);
}
main(argc, argv)
int argc;
char *argv[];
{
word[1]="begin"; word[2]="call"; word[3]="const"; word[4]="do";
word[5]="end"; word[6]="if"; word[7]="odd"; word[8]="procedure";
word[9]="then"; word[10]="var"; word[11]="while";
wsym[1]=beginsym; wsym[2]=callsym; wsym[3]=constsym; wsym[4]=dosym;
wsym[5]=endsym; wsym[6]=ifsym; wsym[7]=oddsym; wsym[8]=procsym;
wsym[9]=thensym; wsym[10]=varsym; wsym[11]=whilesym;
ssym['+']=plus; ssym['-']=minus; ssym['*']=mult; ssym['/']=slash;
ssym['(']=lparen; ssym[')']=rparen; ssym['=']=eql; ssym[',']=comma;
ssym['.']=period; ssym['#']=neq; ssym['<']=lss; ssym['>']=gtr;
ssym['$']=leq; ssym['%']=geq; ssym[';']=semicolon;

f=fopen(*++argv,"r");
kk=al;
cc=0; ll=0; ch=' '; 
getsym();
block(0,0);
getchar();
if (sym!=period) error(9);
getchar();
exit:;
}
