/*
** Small-C Compiler -- Part 2 -- Front End and Miscellaneous.
** Copyright 1982, 1983, 1985, 1988 J. E. Hendrix
** Copyright 1998 H T Walheim
** All rights reserved.
*/

#include <stdio.h>
#include "cc.h"

extern char
 *symtab, *macn, *macq, *pline, *mline,  optimize,
  alarm, *glbptr, *line, *lptr, *cptr, *cptr2,  *cptr3,
 *locptr, msname[NAMESIZE],  pause,  quote[2];

extern int
  *wq,  ccode,  ch,  csp,  eof,  errflag,  iflevel,
  input,  input2,  listfp,  macptr,  nch,
  nxtlab,  op[16],  opindex,  opsize,  output,  pptr,
  skiplevel,  *wqptr;

/********************** input functions **********************/

preprocess() {
  int k;
  char c;
  if(ccode) {
    line = mline;
    ifline();
    if(eof) return;
    }
  else {
    inline();
    return;
    }
  pptr = -1;
  while(ch != NEWLINE && ch) {
    if(white()) {
      keepch(' ');
      while(white()) gch();
      }
    else if(ch == '"') {
      keepch(ch);
      gch();
      while(ch != '"' || (*(lptr-1) == 92 && *(lptr-2) != 92)) {
        if(ch == NULL) {
          error("no quote");
          break;
          }
        keepch(gch());
        }
      gch();
      keepch('"');
      }
    else if(ch == 39) {
      keepch(39);
      gch();
      while(ch != 39 || (*(lptr-1) == 92 && *(lptr-2) != 92)) {
        if(ch == NULL) {
          error("no apostrophe");
          break;
          }
        keepch(gch());
        }
      gch();
      keepch(39);
      }
    else if(ch == '/' && nch == '*')
      {
	bump(2);
	while((ch == '*' && nch == '/') == 0)
	  {
	    if(ch)
	      bump(1);
	    else
	      {
		ifline();
		if(eof)
		  break;
	      }
	  }
	bump(2);
      }
    else  if(ch == '/' && nch == '/')
      {
	bump(2);
	while(ch != NEWLINE)
	  {
	    if(ch)
	      bump(1);
	    else
	      {
		if(eof)
		  break;
	      }
	  }
	bump(1);
      }
    else if(an(ch)) {
      k = 0;
      while(an(ch) && k < NAMEMAX) {
        msname[k++] = ch;
        gch();
        }
      msname[k] = NULL;
      if(search(msname, macn, NAMESIZE+2, MACNEND, MACNBR, 0)) {
        k = getint(cptr+NAMESIZE, 2/*INTSIZE*/);
        while(c = macq[k++]) keepch(c);
        while(an(ch)) gch();
        }
      else {
        k = 0;
        while(c = msname[k++]) keepch(c);
        }
      }
    else keepch(gch());
    }
  if(pptr >= LINEMAX) error("line too long");
  keepch(NULL);
  line = pline;
  bump(0);
  }

keepch(c)  char c; {
  if(pptr < LINEMAX) pline[++pptr] = c;
  }

ifline() {
  while(1) {
    inline();
    if(eof) return;
    if(match("#ifdef")) {
      ++iflevel;
      if(skiplevel) continue;
      symname(msname);
      if(search(msname, macn, NAMESIZE+2, MACNEND, MACNBR, 0) == 0)
        skiplevel = iflevel;
      continue;
      }
    if(match("#ifndef")) {
      ++iflevel;
      if(skiplevel) continue;
      symname(msname);
      if(search(msname, macn, NAMESIZE+2, MACNEND, MACNBR, 0))
        skiplevel = iflevel;
      continue;
      }
    if(match("#else")) {
      if(iflevel) {
        if(skiplevel == iflevel) skiplevel = 0;
        else if(skiplevel == 0)  skiplevel = iflevel;
        }
      else noiferr();
      continue;
      }
    if(match("#endif")) {
      if(iflevel) {
        if(skiplevel == iflevel) skiplevel = 0;
        --iflevel;
        }
      else noiferr();
      continue;
      }
    if(skiplevel) continue;
    if(ch == 0) continue;
    break;
    }
  }

inline() {           /* numerous revisions */
  int k, unit;

  if(input == EOF) openfile();
  if(eof) return;
  if((unit = input2) == EOF) unit = input;
  if(fgets(line, LINEMAX, unit) == NULL) {
    fclose(unit);
    if(input2 != EOF)
         input2 = EOF;
    else input  = EOF;
    *line = NULL;
    }
  else if(listfp)
    {
    if(listfp == output) fputc(';', output);
    fputs(line, listfp);
    }
#ifdef _MSC_VER
  else
    {
      fputc(';', output);
      fputs(line, output);
    }
#endif     
  bump(0);
  }

inbyte()  {
  while(ch == 0) {
    if(eof) return 0;
    preprocess();
    }
  return gch();
  }

/********************* scanning functions ********************/

/*
** test if next input string is legal symbol name
*/
symname(sname) char *sname; {
  int k;char c;
  blanks();
  if(alpha(ch) == 0) return (*sname = 0);
  k = 0;
  while(an(ch)) {
    sname[k] = gch();
    if(k < NAMEMAX) ++k;
    }
  sname[k] = 0;
  return 1;
  }

need(str)  char *str; {
  if(match(str) == 0) error("missing token");
  }

ns()  {
  if(match(";") == 0) error("no semicolon");
  else errflag = 0;
  }

match(lit)  char *lit; {
  int k;
  blanks();
  if(k = streq(lptr, lit)) {
    bump(k);
    return 1;
    }
  return 0;
  }

streq(str1, str2)  char str1[], str2[]; {
  int k;
  k = 0;
  while (str2[k]) {
    if(str1[k] != str2[k]) return 0;
    ++k;
    }
  return k;
 }

amatch(lit, len)  char *lit; int len; {
  int k;
  blanks();
  if(k = astreq(lptr, lit, len)) {
    bump(k);
    return 1;
    }
  return 0;
 }

astreq(str1, str2, len)  char str1[], str2[]; int len; {
  int k;
  k = 0;
  while (k < len) {
    if(str1[k] != str2[k]) break;
    /*
    ** must detect end of symbol table names terminated by
    ** symbol length in binary
    */
    if(str2[k] < ' ') break;
    if(str1[k] < ' ') break;
    ++k;
    }
  if(an(str1[k]) || an(str2[k])) return 0;
  return k;
  }

nextop(list) char *list; {
  char op[4];
  opindex = 0;
  blanks();
  while(1) {
    opsize = 0;
    while(*list > ' ') op[opsize++] = *list++;
    op[opsize] = 0;
    if(opsize = streq(lptr, op))
      if(*(lptr+opsize) != '=' && 
         *(lptr+opsize) != *(lptr+opsize-1))
         return 1;
    if(*list) {
      ++list;
      ++opindex;
      }
    else return 0;
    }
  }

blanks() {
  while(1) {
    while(ch) {
      if(white()) gch();
      else return;
      }
    if(line == mline) return;
    preprocess();
    if(eof) break;
    }
  }

white() {
  return (*lptr <= ' ' && *lptr);
  }

gch() {
  int c;
  if(c = ch) bump(1);
  return c;
  }

bump(n) int n; {
  if(n) lptr += n;
  else  lptr  = line;
  if(ch = nch = *lptr) nch = *(lptr+1);
  }

kill() {
  *line = 0;
  bump(0);
  }

skip() {
  if(an(inbyte()))
       while(an(ch)) gch();
  else while(an(ch) == 0) {
    if(ch == 0) break;
    gch();
    }
  blanks();
  }

endst() {
  blanks();
  return (streq(lptr, ";") || ch == 0);
  }

/*********** symbol table management functions ***********/

addsym(sname, id, type, size, value, lgpp, class)
char *sname, id, type;
int size, value, *lgpp, class;
  {
    if(lgpp == &glbptr)
      {
	if(cptr2 = findglb(sname))
	  return cptr2;
	if(cptr == 0)
	  {
	    error("global symbol table overflow");
	    return 0;
	  }
      }
    else
      {
	if(locptr > (ENDLOC-SYMMAX))
	  {
	    error("local symbol table overflow");
	    exit(ERRCODE);
	  }
	cptr = *lgpp;
      }
    cptr[IDENT] = id;
    cptr[TYPE]  = type;
    cptr[CLASS] = class;
    putint(size, cptr + SIZE, INTSIZE);
    putint(value, cptr + OFFSET, INTSIZE);
    cptr3 = cptr2 = cptr + NAME;
    while(an(*sname))
      *cptr2++ = *sname++;

    if(lgpp == &locptr)
      {
	*cptr2 = cptr2 - cptr3;         /* set length */
	*lgpp = ++cptr2;
      }
    return cptr;
  }

/*
** search for symbol match
** on return cptr points to slot found or empty slot
*/
search(sname, buf, len, end, max, off)
  char *sname, *buf, *end;  int len, max, off; {
  cptr  =
  cptr2 = buf+((hash(sname)%(max-1))*len);
  while(*cptr != NULL) {
    if(astreq(sname, cptr+off, NAMEMAX)) return 1;
    if((cptr = cptr+len) >= end) cptr = buf;
    if(cptr == cptr2) return (cptr = 0);
    }
  return 0;
  }

hash(sname) char *sname; {
  int i, c;
  i = 0;
  while(c = *sname++) i = (i << 1) + c;
  return i;
  }

findglb(sname)  char *sname; {
  if(search(sname, STARTGLB, SYMMAX, ENDGLB, NUMGLBS, NAME))
    return cptr;
  return 0;
  }

findloc(sname)  char *sname;  {
  cptr = locptr - 1;  /* search backward for block locals */
  while(cptr > STARTLOC) {
    cptr = cptr - *cptr;
    if(astreq(sname, cptr, NAMEMAX)) return (cptr - NAME);
    cptr = cptr - NAME - 1;
    }
  return 0;
  }

nextsym(entry) char *entry; {
  entry = entry + NAME;
  while(*entry++ >= ' ');    /* find length byte */
  return entry;
  }

/******** while queue management functions *********/  

addwhile(ptr)  int ptr[]; {
  int k;
  ptr[WQSP]   = csp;         /* and stk ptr */
  ptr[WQLOOP] = getlabel();  /* and looping label */
  ptr[WQEXIT] = getlabel();  /* and exit label */
  if(wqptr == WQMAX) {
    error("control statement nesting limit");
    exit(ERRCODE);
    }
  k = 0;
  while (k < WQSIZ) *wqptr++ = ptr[k++];
  }

readwhile(ptr) int *ptr; {
  if(ptr <= wq) {
    error("out of context");
    return 0;
    }
  else return (ptr - WQSIZ);
 }

delwhile() {
  if(wqptr > wq) wqptr -= WQSIZ;
  }

/****************** utility functions ********************/  

/*
** test if c is alphabetic
*/
alpha(c)  char c; {
  return (isalpha(c) || c == '_');
  }

/*
** test if given character is alphanumeric
*/
an(c)  char c; {
  return (alpha(c) || isdigit(c));
  }

/*
** return next avail internal label number
*/
getlabel() {
  return(++nxtlab);
  }

/*
** get integer of length len from address addr
** (byte sequence set by "putint")
*/
getint(addr, len) char *addr; int len; {
  int i;
  i = *(addr + --len);  /* high order byte sign extended */
  while(len--) i = (i << 8) | *(addr + len) & 255;
  return i;
  }

/*
** put integer i of length len into address addr
** (low byte first)
*/
putint(i, addr, len) char *addr; int i, len; {
  while(len--) {
    *addr++ = i;
    i = i >> 8;
    }
  }

lout(line, fd) char *line; int fd; {
  fputs(line, fd);
  fputc(NEWLINE, fd);
  }

/******************* error functions *********************/  

illname() {
  error("illegal symbol");
  skip();
  }

multidef(sname)  char *sname; {
  error("already defined");
  }

needlval() {
  error("must be lvalue");
  }

noiferr() {
  error("no matching #if...");
  errflag = 0;
  }

error(msg)
char msg[];
  {
    if(errflag)
      return;
    else
      errflag = 1;
    
    lout(line, stderr);
    errout(msg, stderr);
    if(alarm)
      fputc(7, stderr);
    if(pause)
      while(fgetc(stderr) != NEWLINE);
    if(listfp > 0)
      errout(msg, listfp);
  }

errout(msg, fp) char msg[]; int fp; {
  int k;
  k = line+2;
  while(k++ <= lptr) fputc(' ', fp);
  lout("/\\", fp);
  fputs("**** ", fp); lout(msg, fp);
  }