/*               
 * 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.
 *
 * V1.85 July 2000
 * 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, **blk_run_link;
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;
	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 size = 0;
	
	TABLE *dest = &sym->tp->lst;

	if (from) {
		SYM *head=from->tp->lst.head;
    sym->value.classdata.vtabsize = from->value.classdata.vtabsize;
    sym->value.classdata.size = from->value.classdata.size;
    sym->value.classdata.vtablist = from->value.classdata.vtablist;
		while (head) {
			SYM *sp = copysym(head);
			/* guaranteed not to have classes in classes */
			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 (sym->value.classdata.basemode == BM_PRIVATE) {
				sp->value.classdata.cppflags &= ~(PF_PUBLIC | PF_PROTECTED);
				sp->value.classdata.cppflags |= PF_PRIVATE;
			}
			else {
				if (sym->value.classdata.basemode == 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 0;
	}
  sym->value.classdata.vtabsize = vtabhead;
  sym->value.classdata.size = classhead;
  return 0; 
}
void loadclassdata(SYM *sp)
{
  SYM *head = sp->value.classdata.baseclass;
  LIST *lst, *lst1 = 0, **lst2= &lst1;
  copydata(sp,head);
  lst = sp->value.classdata.vtablist;
  /* now copy the vtab */
  while (lst) {
    (*lst2) = xalloc(sizeof(LIST));
    (*lst2)->data = lst->data;
    lst2 = &((*lst2)->link);
    lst = lst->link;
  }
  sp->value.classdata.vtablist = lst1;
  
}
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 = (SYM *)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,*baseclass = sp->value.classdata.baseclass,**tab;
	int i;
	int hasdecl;
	char *name;
	if (constree && sp->value.classdata.basecons)
		if (sp->value.classdata.basecons != constree)
			generror(ERR_CONSDECLNOMATCH,0,0);
	if (inittab) {
		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 {
		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);
		}
	}
	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;
																	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);
	if (!(sp->value.classdata.cppflags & PF_STATIC))
		rv = makenode(en_thiscall, 0, rv);
	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);
    switch( spc->storage_class ) {
      case sc_static:
      case sc_global:
      case sc_external:
      case sc_externalfunc:
			case sc_abs:
				spc->extflag = TRUE;
				if (spc->staticlabel)
					qnode = makenode(en_nalabcon,(char *)spc->value.i,0);
				else
        	qnode = makenode(en_nacon,spc,0);
        break;
      default:        /* auto and any errors */
				if (spc->storage_class == sc_auto)
       		qnode = makenode(en_autocon,spc,0);
				else if (spc->storage_class == sc_autoreg)
       		qnode = makenode(en_autoreg,spc,0);
  	}
		if (spc->tp && !spc->tp->val_flag)
			deref(qnode,spc->tp);
		if (spc->isstdcall)
	   	rv = makenode(en_sfcall,qnode,rv);
		else
	   	rv = makenode(en_fcall,qnode,rv);
		rv = makenode(en_void,rv,0);
/* create the destructor list in backwards order */

		if (!block_rundown)
			block_rundown = rv;
		else
			*blk_run_link = rv;
		blk_run_link = &rv->v.p[1];
}
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);
		sp = search(cpp_funcname_tab[CI_CONSTRUCTOR], sp->tp->lst.head);
		if (sp)
			sp = funcovermatch(sp,tp1);
		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->name);
		genlong(0); /* space for exception handling */
    while (lst) {
      genpcref((SYM *)lst->data,0);
      lst = lst->link ;
		}
		gen_endvirtual(sp->value.classdata.vtabsp->name);
	}
}
#endif
