/*               
 * 68K/386 32-bit C compiler.
 *
 * copyright (c) 1997, David Lindauer
 * 
 * This compiler is intended for educational use.  It may not be used
 * for profit without the express written consent of the author.
 *
 * It may be freely redistributed, as long as this notice remains intact
 * and either the original sources or derived sources 
 * are distributed along with any executables derived from the originals.
 *
 * The author is not responsible for any damages that may arise from use
 * of this software, either idirect or consequential.
 *
 * V2.05 June 2002
 * David Lindauer, camille@bluegrass.net
 *
 * Credits to Mathew Brandt for original K&R C compiler
 *
 */
#include        <stdio.h>
#include				<limits.h>
#include 				<string.h>
#include        "lists.h"
#include        "expr.h"
#include        "c.h"
#include        "errors.h"

extern char *cpp_funcname_tab[];
extern int lastst;
extern int skm_declcomma[];
extern int skm_declclosepa[];
extern int stdaddrsize;       
extern ENODE *block_rundown ;
extern SYM *declclass;
extern char lastid[];
extern TYP stdmatch;
extern TABLE lsyms;
extern SYM undef;
extern int global_flag;
extern long nextlabel;
int classhead;
int vtabhead;
static TABLE vmptab;
static TYP             voidtype = { bt_void, 0, 0 ,-1, -1, 4};

void defclassini(void)
{
   vtabhead = stdaddrsize*2;
	classhead = stdaddrsize;
	vmptab.head = 0;
}
SYM *copysym(SYM *sp)
{
	SYM *rv = xalloc(sizeof(SYM));
	*rv = *sp;
	return rv;
}
#ifdef CPLUSPLUS
static int copydata(SYM *sym, SYM *from, int mode)
{
   int size = sym->value.classdata.size ;
	
	TABLE *dest = &sym->tp->lst;

	if (from) {
		SYM *head=from->tp->lst.head;
		while (head) {
         SYM *sp = copysym(head);
         sp->next = 0 ;
         sp->parentclass = sym ;
         if (sp->value.classdata.cppflags & PF_VIRTUAL) 
            sp->value.classdata.cppflags |= PF_NODEF;
         if (sp->value.classdata.cppflags & PF_PRIVATE)
            sp->value.classdata.cppflags |= PF_UNREACHABLE;

         if (sp->tp->type == bt_func || sp->tp->type == bt_ifunc)
            sp->value.classdata.vtaboffs+= size ;
         else if (sp->tp->type == bt_defunc) {
            SYM *head2 = dest->head;
            SYM *head3 = sp->tp->lst.head ;
            int retagged = 0 ;
            while (head2) {
               if (!strcmp(head2->name,sp->name) && head2->tp->type == bt_defunc) {
                  sp = head2 ;
                  retagged = TRUE ;
                  break ;
               }
               head2 = head2->next ;
            }
            if (!retagged) {
               sp->tp = cponetype(sp->tp) ;
               sp->tp->sp = sp ;
               sp->tp->lst.head = sp->tp->lst.tail = 0 ;
            }
            head2 = head3 ;
            while (head2) {
               if (!(head2->value.classdata.cppflags & (PF_CONSTRUCTOR | PF_DESTRUCTOR))) {
                  SYM *s = copysym(head2) ;
                  s->value.classdata.vtaboffs+= size ;
                  if (sp->tp->lst.head)
                     sp->tp->lst.tail = sp->tp->lst.tail->next = s ;
                  else
                     sp->tp->lst.head = sp->tp->lst.tail = s ;
                  s->next = 0 ;
               }
               head2 = head2 -> next ;
            }
            if (retagged || sp->tp->lst.head == 0) {
               head = head->next ;
               continue ;
            }
         } else
            sp->value.i += size ;

         if (mode == BM_PRIVATE) {
            sp->value.classdata.cppflags &= ~(PF_PUBLIC | PF_PROTECTED);
            sp->value.classdata.cppflags |= PF_PRIVATE;
         }
         else {
            if (mode == BM_PROTECTED &&
                     sp->value.classdata.cppflags & PF_PUBLIC) {
               sp->value.classdata.cppflags &= ~PF_PUBLIC; 
               sp->value.classdata.cppflags |= PF_PROTECTED;
            }
         }
         if (!dest->head)
            dest->head = dest->tail = sp;
         else 
            dest->tail = dest->tail->next = sp;
			head = head->next;
		}
	}
  return size ; 
}
int loadclassdata(SYM *sp, SYM *spc, int mode) 
{
  CLASSLIST *l = spc->value.classdata.baseclass, *lo=sp->value.classdata.baseclass, **lp=&lo ;
  int rv = copydata(sp,spc,mode);
  while (*lp)
      lp = & (*lp)->link ;
  while (l) {
      *lp = xalloc(sizeof(CLASSLIST)) ;
      (*lp)->data = l->data ;
      (*lp)->offset = l->offset + rv ;
      lp = &(*lp)->link ;
      l = l->link ;
  }
  *lp = xalloc(sizeof(CLASSLIST)) ;
  (*lp)->data = spc ;
  (*lp)->offset = rv ;
  sp->value.classdata.baseclass = lo ;
  sp->value.classdata.vtaboffs = sp->value.classdata.size = rv + spc->value.classdata.size ;
  sp->value.classdata.vtabsize = vtabhead ;
  return rv + spc->value.classdata.size;
}
static char *findname(char *buf)
{
  while (TRUE) {
    char *buf1 = strchr(buf,'$');
    if (!buf1)
      return FALSE;

    if (*(buf1 - 1) != '@') {
      while (*(buf1-1) != '@' && buf1 != buf)
        buf1--;
      return buf1;
    }
  }
}
static int vtabmatch(SYM *candidate, SYM *current)
{
  char *cbuf = candidate->name;
  char *ubuf = current->name;
  if (!exactype(candidate->tp, current->tp))
    return FALSE ;
  cbuf = findname(cbuf);
  ubuf = findname(ubuf);
  if (!cbuf || !ubuf)
    return FALSE;
  return (!strcmp(cbuf,ubuf));
}
void addvtabentry(SYM *cl, SYM *sp)
{
  LIST **lst2 = &cl->value.classdata.vtablist,*lst = *lst2;
  while (lst) {
    SYM *sp1 = lst->data;
    if (vtabmatch(sp,sp1)) {
      sp->value.classdata.vtabindex = sp1->value.classdata.vtabindex;
      lst->data = sp;
      return ;
    }
    lst2 = &(lst->link);
    lst = lst->link;
  }
  /* get here if no entry matched */
  lst = (*lst2) = xalloc(sizeof(LIST));
  lst->link = 0;
  lst->data = sp;
  sp->value.classdata.vtabindex = cl->value.classdata.vtabsize;
  cl->value.classdata.vtabsize +=stdaddrsize;
}
void classerrs(SYM *sp)
{
	SYM *head = sp->tp->lst.head;
	int hascons = FALSE,hasref = FALSE,hasfunc = FALSE;
	while (head) {
		if (head->value.classdata.cppflags & PF_CONSTRUCTOR) {
			hascons = TRUE;
			sp->value.classdata.cppflags |= PF_HASCONS;
		}
		if (head->value.classdata.cppflags & PF_DESTRUCTOR) {
			sp->value.classdata.cppflags |= PF_HASDEST;
		}
		if (head->tp->type == bt_ref)
			hasref = TRUE;
		head->parentclass = sp;
		head = head->next;
	}
	if (hasref && !hascons) {
		gensymerror(ERR_REFNOCONS,sp->name);
	}
}
void consreferr(SYM *sp, ENODE *constree, SYM **inittab, int count)
{
   SYM *head,**tab;
	int i;
	int hasdecl;
	char *name;
   LIST *bcl = sp->value.classdata.baseclass ;
	if (constree && sp->value.classdata.basecons)
		if (sp->value.classdata.basecons != constree)
			generror(ERR_CONSDECLNOMATCH,0,0);
   while (bcl) {
      SYM *baseclass = bcl->data ;
      if (inittab && baseclass) {
         for (tab = inittab; count; count--,tab++) {
            if (!strcmp((*tab)->name,baseclass->name)) {
               head = baseclass->tp->lst.head;
               while (head) {
                  if (!strcmp(sp->name,head->name)) {
                     hasdecl = TRUE;
                     break;
                  }
                  head = head->next;
                  if (!hasdecl)
                     gensymerror(ERR_NOBASECONS,baseclass->name);
               }
               
               break;
            }
         }
      } else if (baseclass) {
         head = baseclass->tp->lst.head;
         while (head) {
            if (!strcmp("@$ctr$qv",head->name)) {
               hasdecl = TRUE;
               break;
            }
            head = head->next;
            if (!hasdecl)
               gensymerror(ERR_NOBASECONS,baseclass->name);
         }
		}
      bcl = bcl->link ;
	}
	if (inittab) {
		head = sp->tp->lst.head;
		while (head) {
			hasdecl = FALSE;
			if (head->tp->type == bt_ref) {
				for (tab = inittab; count; count--,tab++)
					if ((*tab)->tp->type == bt_ref) {
						if (!strcmp(head->name,(*tab)->name)) {
							hasdecl = TRUE;
							break;
						}
					}
				if (!hasdecl)
					gensymerror(ERR_CONSNOREFINIT,head->name);
			}
		}
		head = head->next;
	}
}
TYP *classnameref(ENODE **node, SYM **pos)
{       SYM             *sp;
        TYP             *tp,*tp1;
				char *nm;
				int fn = FALSE;
				char buf[100];
				strcpy(buf,lastid);
				getsym();
				nm = litlate(buf);
				sp = search(nm,&declclass->tp->lst);
        if( sp == 0 ) {
												sp = xalloc(sizeof(SYM));
                				sp->name = nm;
                        sp->tp = tp = &stdmatch;
												sp->storage_class = sc_external;
												insert(sp,&lsyms);
												*node = makenode(en_nacon,&undef,0);
												gensymerror(ERR_UNDEFINED,nm);
                        tp = deref(node,tp);
        } else    {
								*pos = sp;
								sp->tp->uflags |= UF_USED;
                if( (tp = sp->tp) == 0 ) {
/* This lack of type info should never happen */
                        tp = &stdmatch;
												*node = makenode(en_nacon,&undef,0);
												gensymerror(ERR_UNDEFINED,nm);
                        tp = deref(node,tp);
                        return tp;       /* guard against untyped entries */
                        }
                switch( sp->storage_class ) {
                        case sc_static:
                        case sc_global:
                        case sc_external:
                        case sc_externalfunc:
												case sc_abs:
																sp->extflag = TRUE;
#ifdef CPLUSPLUS
																if (sp->value.classdata.gdeclare)
																	sp->value.classdata.gdeclare->extflag = TRUE ;
#endif
																	if (sp->absflag)
                                		*node = makenode(en_absacon,sp,0);
																	else
																		if (sp->tp->type == bt_func || sp->tp->type == bt_ifunc) {
																			fn = TRUE;
                                			*node = makenode(en_napccon,sp,0);
																		}
																		else
																			if (sp->staticlabel)
																				*node = makenode(en_nalabcon,(char *)sp->value.i,0);
																			else
                                				*node = makenode(en_nacon,sp,0);
                                break;
                        default:        /* auto and any errors */
                                if( sp->storage_class != sc_auto && sp->storage_class != sc_autoreg) {
                                        gensymerror(ERR_ILLCLASS2,sp->name);
																				tp = 0;
																}
																else {
/* auto variables */
															 		if (sp->storage_class == sc_auto)
                                		*node = makenode(en_autocon,sp,0);
																	else if (sp->storage_class == sc_autoreg)
                                		*node = makenode(en_autoreg,sp,0);
																}
                                break;
                        }
/* dereference if it isn't an array or structure address */

								if (tp) {
									(*node)->cflags = tp->cflags;
								/* deref if not an array or if is function parm */
  	              if(!fn && tp && (tp->val_flag == 0 || (sp->funcparm && tp->type == bt_pointer)))
  	                     tp = deref(node,tp);
/* and dereference again if it is a refernece variable */
	  							if (tp->type == bt_ref) {
												tp = tp->btp;
												tp = deref(node,tp);
		  						}
												
								}
							}
        return tp;
}
ENODE *conscall(SYM *sp, ENODE* ep)
{
	ENODE *rv,*qnode;
  if( sp->storage_class != sc_auto && sp->storage_class != sc_autoreg) {
    gensymerror(ERR_ILLCLASS2,sp->name);
		rv = 0;
	}
	else {
/* auto variables */
		if (sp->storage_class == sc_auto)
  		rv = makenode(en_autocon,sp,0);
		else if (sp->storage_class == sc_autoreg)
   		rv = makenode(en_autoreg,sp,0);
  }
	rv = makenode(en_void,rv,ep);
	qnode = makenode(en_icon,(char *)sp->tp->btp->size,0);
	if (isstructured(sp->tp->btp)) {
		if (sp->isstdcall)
	   	rv = makenode(en_sfcallb,qnode,rv);
		else
	   	rv = makenode(en_fcallb,qnode,rv);
		(rv)->size = sp->tp->btp->size;
	}
	else {
		if (sp->isstdcall)
	   	rv = makenode(en_sfcall,qnode,rv);
		else
	   	rv = makenode(en_fcall,qnode,rv);
	}
	if (rv->nodetype == en_fcallb || rv->nodetype == en_sfcallb)
		rv = make_callblock(0,rv,sp->tp,sp->tp->size);
	rv = makenode(en_thiscall, rv, 0);
	return rv;
}
void destcall(SYM *spc)
{
	ENODE *rv,*qnode;
/* auto variables */
		if (spc->storage_class == sc_auto)
  		rv = makenode(en_autocon,spc,0);
		else if (spc->storage_class == sc_autoreg)
   		rv = makenode(en_autoreg,spc,0);
		qnode = makenode(en_icon,(char *)spc->tp->btp->size,0);
		if (spc->isstdcall)
	   	rv = makenode(en_sfcall,qnode,rv);
		else
	   	rv = makenode(en_fcall,qnode,rv);
		rv = makenode(en_thiscall, rv, 0) ;
		rv = makenode(en_void,rv,block_rundown);
/* create the destructor list in backwards order */

		block_rundown = rv ;
}
TYP * basedecl(ENODE **rv, SYM *sp)
{
	SYM *sp2;
	TYP *tp1;
	char *name;
	int opened = FALSE;
	*rv = 0;
	if (lastst == openpa) {
		opened = TRUE;
		getsym();
	}
	else
		if (lastst == assign)
			getsym();
		else {
			/* base class declare, no constructor specified */
			name = fullcppmangle(0,&cpp_funcname_tab[CI_CONSTRUCTOR],&voidtype);
			sp2 = search(name,&sp->tp->lst);
			if (sp->value.classdata.cppflags & PF_HASCONS) {
				if (!sp2)
					genfunc2error(ERR_NOFUNCMATCH,sp->name,name);
				*rv = conscall(sp2,0);
			}
			if (sp->value.classdata.cppflags & PF_HASDEST) {
				if (!sp2)
					genfunc2error(ERR_NOFUNCMATCH,sp->name,name);
				destcall(sp2);
			}
			return &voidtype;
		}
			
	if (sp->tp->type == bt_class) {
		tp1 = gatherparms(rv,FALSE);
		sp = search(cpp_funcname_tab[CI_CONSTRUCTOR], sp->tp->lst.head);
		if (sp)
         sp = funcovermatch(sp,tp1,FALSE);
		if (!sp2) {
			genfunc2error(ERR_NOFUNCMATCH,sp->name,name);
			tp1 = 0;
		}
		else {
			rv = conscall(sp2,*rv);
			tp1 = sp2->tp;
			destcall(sp2);
		}
	}
	else {
		if ((tp1 = autoasnop(rv, sp)) == 0) {
			generror(ERR_EXPREXPECT,0,0);
			getsym();
		}
	}
	if (opened)
		needpunc(closepa,skm_declclosepa);
	return tp1;
}
void classbaseasn(SYM *sp)
/*
 *      handles a list of constructor base assignment operators
 */
{       ENODE    *ep1,*ep2,*nodep;
        TYP      *tp1,*tp2;
				SYM *inits[256];
				int initcount = 0;
				ENODE *constree=0;
				char *id;
				while (TRUE) {
					if (lastst != id) {
						generror(ERR_IDEXPECT,0,0);
						basicskim(skm_declcomma);
					} else {
						tp1 = classnameref(&ep1,&inits[initcount]);
						tp2 = basedecl(&ep2,inits[initcount]);
						constree = makenode(en_void,ep2,constree);
						initcount++;
					}
					if (lastst != comma)
						break;
				}
	consreferr(sp,constree,inits,initcount);
	sp->value.classdata.basecons = constree;
}
void gen_vtab(SYM *sp)
{
	SYM *head;
  LIST *lst;
  if (lst = sp->value.classdata.vtablist) {
		gen_virtual(sp->value.classdata.vtabsp);
		genlong(0); /* space for exception handling */
    while (lst) {
         SYM *sp = lst->data ;
			sp->extflag = TRUE ;
      genpcref(sp,0);
      lst = lst->link ;
		}
		gen_endvirtual(sp->value.classdata.vtabsp);
	}
}
#endif
