/* This is -*- C++ -*-.
   Copyright (C) 1992 Per Bothner.

This file is part of Q.

Q is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

Q is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <stdio.h>
#include <stdarg.h>
#include <types.h>
#include <parsefile.h>
#include <machines.h>
#include <debug.h>
//#include <format.h>
#include <exceptions.h>
#include <Qcompile.h>
#include <traverse.h>
#include <newtype.h>
#include <string.h>
extern struct Label *MakeSymbolPtr();
EXTERN void AddGlobalSymbol(char *name, void *value);
extern struct Type *FindExprType();
extern Name FindClosestSymbol();
extern Object Apply(), tmalloc(), _release_(), _combine_(), InsertProcInType(),
    Insert_Proc(), _Raise_();
EXTERN void * LookupGlobalLabel(char *name, int *found);
extern int CompileExpr();
extern struct DataToken *GetMemTempToken();
#define CompileDebug
struct DataToken ZeroToken[1] = {{ConstToken, LiteralToken}};
/* struct Label DirectPut[1] = {{0, 0, 0, 0}}; */
struct PrevCompiled *PrevCompiledList;

#define OutComment "|"
#define Push1(arg1) arg1;
#define Push2(arg1, arg2) arg2;arg1;
#define Push3(arg1, arg2, arg3) arg3;arg2;arg1;

ostream& operator<<(ostream& outs, Label& lab)
{
    char buf[20];
    switch (lab.kind) {
	case GenLabelToken:
	    GenLabToString(buf, (int)lab.label);
	    outs << buf;
	    break;;
	case ConstToken:
	    outs << lab.label;
	    break;
	default: abort();
    }
    return outs;
}

static LoopCount = 0;

Symbol * GenName()
{
    char buf[40];
    sprintf(buf, "__TMP__%d__", ++LoopCount);
    return EnterSymbol(buf);
}

/* Duplication here! */

char *GenString(int i = -1)
  { char buf[20];
    static lastId = 0;
    if (i < 0) { i = lastId; lastId++; }
    sprintf(buf, "_Id_%d", i);
    return strdup(buf);
  }

#if 0
void MakeGenLabel(struct Label *lab, int i)
  {
    MakeGenLabelToken(lab, i, 0, 0);
  }

void MakeAbsLabel(struct Label *labm CFile *cf)
{
    MakeGenLabel(lab, cf->generate_label_number() + 1);
}
#endif

struct DataToken *
MakeLabelToken(register struct DataToken *token,
	       char *label,
	       int offset,
	       int indirect) /* 0: immediate, 1: get memory, 2: indirect etc */
  {
    token->kind = ConstToken;
    token->dependents = NULL;
    if (indirect == 0)
	token->options = LiteralToken;
    else if (indirect == 1)
	token->options = 0;
#if 1
    token->u.constant.label = label;
#else
    if (label != NULL)
	token->u.constant.label = label;
    else if (offset > 0)
	token->u.constant.label = SegTable[DataSegment].startLabelSuffix;
    else
      {
	offset = -offset;
	token->u.constant.label = SegTable[DataSegment].startLabelSuffix;
      }
#endif
    token->u.constant.offset = offset;
    return token;
  }

void CompileError(CFile *cf, char *format, ...)
{
    va_list args;
    int level = CheckErrorLevel(format);
    struct Location *sourcePos;
    if (level >= 0) format += 2;
    else level = ErrMessage;
    if (cf == NULL)
	sourcePos = NULL;
    else
      {
	sourcePos = &cf->sourcePos;
	if (level > cf->errors) cf->errors = level;
      }
    va_start(args, format);
    ErrorPrint(CompileTimeErr, level, sourcePos, stderr, format, args);
    va_end(args);
}

#if 0
int
CompStdPrefix(cf, label, type)
    CFile *cf; struct Label *label; struct Type *type;
  {
    if (label == DirectPut)
      {
	if (type->prefixLen > sizeof(struct Type*))
	    CompileError(cf, "Type %s has prefix fields but dumped directly",
		type->name);
      }	
    else
      {
#if 1
	cf->asm_stream() << "asm(\" .data\");\n";
	if (type->prefixLen <= sizeof(struct Type*))
	    CompBlockPrefix(cf, type);
	CompileLabelTo(cf, type);
#else
	int best_offset = 0; Name type_label;
	if (type->prefixLen <= sizeof(struct Type*))
	    CompBlockPrefix(cf, type);
	type_label = type->name;
	if (type_label == NULL) /* NOTE: kludge! */
	  {
	    type_label = FindClosestSymbol(type, &best_offset);
	    if (type_label == NULL)
		CompileLabelTo(cf, type);
	  }
	if (type_label != NULL) CompPtr(cf, type_label, 0, 0);
	if (label)
	    CompLab(cf, label);
#endif
      }
    return cf->segSize[cf->segment];
  }

int
CompileStdPrefix(cf, label, type)
    CFile *cf; struct Label *label; char *type;
  {
    CompPtr(cf, type, 0, 0);
    if (label)
      {
	CompLab(cf, label);
      }
    return cf->segSize[cf->segment];
  }
#endif

char CompileLoadCode[100] = "";

#if 0
StdPutFields(CFile *cf, Object object, struct Type *type)
{   register struct Declaration *field;
    register char *top = (char*)object;
    int offset = 0;
    struct ClassDesc *desc = type->desc;
    struct Field *fld;
    FOR_EACH_FIELD(fld, desc) {
	if (fld->kind != Pointer_Field && fld->kind != Include_Field)
	    continue;
	if (fld->offset == -1) continue;
	CompNByte(cf, fld->offset - offset, top+offset, NULL);
	offset = fld->offset;
	if (fld->kind == Pointer_Field) {
	    CompileLabelTo(cf, *(Object*)(top+offset));
	    offset += sizeof(Object);
	}
	else /* DeclIsInclude(field) */ {
	    DumpObject(top+offset, fld->u.type, cf, DirectPut);
	    offset += fld->u.type->size;
	}
    }
    CompNByte(cf, type->size - offset, top+offset, NULL);
    return;
}
#endif

#if 0
void DumpObject(register Object x, Type *type, CFile *cf, Label *label)
  {
    register Func cputFunc;
    extern struct Function *FuncTabSearch();
    struct Function *func =
	FuncTabSearch(type->desc, sDumpData);
    if (func != NULL) {
	(*(IntMethod)func->clauses->code)(MAKE_ANY(x,type), cf, label);
	return;
    }
    if (type == BlockT)
      {
	DumpBlockType(x, cf, label);
	return;
      }
  }
#endif

struct PrevCompiled *
BindPrevCompiled(CFile *cf, Object value,
		 register struct Label *label /* is copied. */)
  { register struct PrevCompiled *p =
	(struct PrevCompiled*)malloc(sizeof(struct PrevCompiled));
    p->value = value;
/* the next statement is mis-compiled unless label is a register ! */
    p->lab = *label;
    p->next = PrevCompiledList;
    if (label->kind == StringLabel)
	p->lab.label = label->label == NULL ? NULL : strdup(label->label);
    PrevCompiledList = p;
    return p;
  }

#if 0
void DumpGen(struct Any val, CFile *cf, struct Label *label)
{
    struct Label lab;
#if 0
    if (AnyIsVar(val) && IsBound((Var*)val.addr))
	val = ((Var*)val.addr)->any;
    if (val.type == PtrT) val.type = MemGetType(val.addr);
    if (val.addr == NULL) return NULL;
    if (SearchPrevCompiled(&lab, val.addr, cf))
      {
	if (label == 0) ;
	else if (!CompileToC(cf))
	    CompLabelEqual(cf, label, &lab);
	else {
	    cf->asm_stream() << "#define " << *label;
	    cf->asm_stream() << ' ' << lab << '\n';
	}
	return;
      }
#endif
    if (label == NULL)
      {
	SetOffsetLabel(&lab, cf->segment);
	label = &lab;
      }
    else if (!IsOffsetLabel(label))
	BindPrevCompiled(cf, val.addr, label);
    return DumpObject(val.addr, val.type, cf, label);
  }

CPutGen(x, cf, label)
    Object x; CFile *cf; struct Label *label;
{   DumpGen(MAKE_ANY(x, MemGetType(x)), cf, label); }

int SearchPrevCompiled(struct Label *token, Object value, CFile *cf)
 /* if value is already compiled, set token to its address, and return 1 */
  {
    register struct PrevCompiled *p;
    extern char _end[];
    if (value == NULL) return 0;
    if (HasHType(value, Symbol)) {
	*token = *MakeSymbolPtr(cf, value); return 1; }
    if ((char*)value < _end) {
	int best_offset = 0;
	Name name = FindClosestSymbol(value, &best_offset);
	if (name == NULL)
	    CompileError(cf, "Pointer to old value #%x with no label", value);
	MakeLabelToken(token, name, best_offset, 0);
	return 1;
    }
    for (p = PrevCompiledList; p; p=p->next)
	if (p->value == value) { *token = p->lab; return 1; }
    return 0;
  }
#endif

static int DumpPendingLevel = 0;

void DumpPending(register CFile& cf)
{
    if (DumpPendingLevel) // Avoid recursive calls.
	return;
    DumpPendingLevel++;
    DoPendingDumps(&cf);
#if 0
  /* note care needed since CPutGen could add other values to pendingList */
    register struct PendingValue *p;
     while ((p = cf.pendingList) != NULL)
      {
	cf.pendingList = p->next;
	abort();
	if (p->value.type == NULL)
	    CPutGen(p->value.addr, &cf, &p->lab);
	else {
	    extern Func FuncTabSearch();
	    struct Function *func = (struct Function*)
		FuncTabSearch(p->value.type->desc, sDumpData);
	    if (func == NULL)
		CompileError(&cf,
			     "Confusion in DumpPending - missing %s method",
			     SymbolString(sDumpData));
	    else (*(IntMethod)func->clauses->code)(p->value, &cf, &p->lab);
	}
	/* NOTE: should Release(p->value) */
	free(p);
      }
#endif
    DumpPendingLevel--;
}

#if 0
void SavePendingValue(CFile *cf, struct DataToken *label, struct Any value)
  { register struct PendingValue *p =
	(struct PendingValue*)malloc(sizeof(struct PendingValue));
    p->value = value;
/*  p->value.type = NULL; */
    p->lab = *label;
    p->next = cf->pendingList;
    cf->pendingList = p;
  }

static void MakePendingLabel(CFile *cf, struct DataToken *label, Object value)
{
    struct Any any;
    any.addr = value; any.type = NULL;
    SavePendingValue(cf, label, any);
}
#endif

void MakeTokenTo(CFile *cf, struct DataToken *token, Object value)
 /* Make 'token' be a referent for 'value'.
  * If token->kind == NullToken, set it to a suitable value.
  */
  {
#if 1
    abort();
#else
    struct DataToken tmp[1];
    if (EvalCompile(cf) && LogCode <= 0)
	MakeIntToken(tmp, value); /* only if we're not dumping assembler */
    else if (SearchPrevCompiled(tmp, value, cf)) ;
    else if (value == NULL || EvalCompile(cf))
	MakeIntToken(tmp, value);
    else
      { struct PendingValue *pnd = cf->pendingList;
	for (; ; pnd = pnd->next)
	    if (!pnd) {
		if (token->kind == NULL)
		    MakeGenLabel(token, cf->generate_label_number());
		MakePendingLabel(cf, token, value);
		return;
	    } else if (pnd->value.addr == value)
		break;
	*tmp = pnd->lab;
      }
    if (token->kind == NullToken) *token = *tmp;
    else CompLabelEqual(cf, token, tmp);
#endif
  }

#if 0
CompileLabelTo(cf, x)
    CFile *cf;
  {
    struct DataToken token;
    token.kind = NullToken;
    MakeTokenTo(cf, &token, x);
    CompTokenPtr(cf, &token, 0);
  }

Compile2LabelsTo(cf, x)
    CFile *cf; Object *x;
  {
    struct DataToken tokens[2];
    tokens[0].kind = NullToken;
    tokens[1].kind = NullToken;
    MakeTokenTo(cf, &tokens[0], x[0]);
    MakeTokenTo(cf, &tokens[1], x[1]);
    CompNTokenPtr(cf, 2, 0, tokens);
  }

CompileLoadValue(value, cf, dest)
    CFile *cf; struct DataToken *dest;
  { struct DataToken token;
    token.kind = NullToken;
    MakeTokenTo(cf, &token, value);
    CompileMove(cf, &token, dest);
  }

CompileSymbol(sym, cf, dest)
    CFile *cf; struct DataToken *dest;
  {
    Object val = Search(Env, sym); struct DataToken tmp_token[1];
    if (val == NoValue)
	CompileError(cf, "Warning: identifier %s not declared", sym);
    if (HasHType(val, ExceptionClass))
	CompRaise(cf, val, NULL);
    else if (EvalCompile(cf))
      {
	CompileLoadInt(cf, Env);
	CompileLoadInt(cf, sym);
	AddressLabelToken(tmp_token, cf, "typestructs_EvalSymbol", 0);
	CompCall(cf, tmp_token, QFuncRegMask, -1, dest);
      }
    else
      {
	MakeLabelToken(tmp_token, sym, 0, 0);
	CompileMove(cf, tmp_token, dest);
      }
  }
 
CompileDeclaration(decl, cf, dest)
    register struct Declaration *decl; CFile *cf; struct DataToken *dest;
  {
    CompileError(cf,
	"Illegal internal call: CompileDeclaration(#%X=%s, #%X, #%X)",
	decl, decl->name, cf, dest);
  }

CompileLoadName(cf, str)
    CFile *cf; char *str;
  {
    int pos;
    if (EvalCompile(cf) && LogCode <= 0)
	CompileLoadInt(cf, str);
    else
	CompileMove(cf, MakeSymbolPtr(cf, str), PushStackToken);
  }
#endif

// static _dummy() { bcopy(); } /* to force linking */

extern char BadCompileForEval[];
#ifdef M68K
asm(" .globl BadCompileForEval");
asm("BadCompileForEval:");
asm(" movl #NoValue,a5");
asm(" rts");
#endif

#if 0
char *
CompileForEval(ex, data)
    struct Block *ex;
    struct TraverseData *data;
  {
    struct ProcExpr *fnc;
    struct Module *module = data->curModule;


    TraverseForEval(ex, data);
    if (data->errors > WrnMessage)
	return BadCompileForEval;

#if 0
    if (module->syms != ex->locals)
	if (module->syms == NULL) module->syms = ex->locals;
	else abort();
#endif

    fnc = GC_NEW ProcExpr(ex);
    return CompileProcForEval(fnc);
  }
#endif

#if 0
CompileProcForEval(fnc)
    struct ProcExpr *fnc;
  {
    register char *ptr; /* a5 */
    int segment;
    struct ExtSymbol *sym;
    int badLabels = 0;
    char *codeArray;
    register struct Relocation *reloc;
    struct Relocation *nextReloc;
    struct Label startLabel;
    register CFile *cf;
    register i;

    cf = new CFile(0, 0. 0);
    SetOffsetLabel(&startLabel, TextSegment);
    CompileProc(fnc, cf, &startLabel);
    cf->finish();
    if (cf->errors > WrnMessage)
	return BadCompileForEval;
    codeArray = (char*)malloc(cf->segSize[TextSegment]);

 /* define the globals */
    for (sym = cf->firstSymbol; sym; sym = sym->next)
	if (sym->s.stype & EXTERN)
	  {
	    switch (sym->s.stype & ~EXTERN)
	      {
	      case ABS:
		ptr = (char*)sym->s.svalue;
		break;
	      case TEXT:
		ptr = codeArray + sym->s.svalue;
		break;
	      default:
		fprintf(stderr, "CompileForEval: Cannot make %s global",
			(Name)(sym+1));
		ptr = 0;

	      }
	    AddGlobalSymbol(CopyString((char*)(sym+1), (void*)ptr);
	  }

#if 0
 /* allocate pending Type descriptors */
      { register struct TypeDefExpr *typeDef = cf->pendingTypeDefs;
	for (; typeDef != NULL; typeDef = typeDef->next)
	    PatchTypeDef(typeDef, cf);
      }
#endif

 /* write text segment
    This is complicated by the fact that we must add the text size to
    to all relocatable words that refer to the data segment.
    Also, we must check if labels have become defined.
 */
    segment = TextSegment;
    ptr = codeArray;
      { register FILE *segFile = cf->segFile[segment];
	int rpos = 0;
	int more = cf->segSize[segment];
	reloc = cf->firstReloc[segment];
	fseek(segFile, 0, 0);
	for (;reloc;)
	  { long adjust = 0;

	 /* if we relocate relative to a known label, fix this */
	    if (reloc->rsegment == REXT)
	      {
	        sym = reloc->rsymbol;
		if (sym->s.stype != UNDEF)
		  { 
		    reloc->rsymbol = 0;
		    adjust = sym->s.svalue;
		    if (sym->s.stype == ABS)
			adjust -= (int)codeArray;
		  }
		else {
		    int found;
		    char *ob = (char*)LookupGlobalLabel((char*)sym+1, &found);
		    if (!found)
		      {
			fprintf(stderr,
			    "CompileForEval: Label %s not defined\n", sym+1);
			badLabels++;
		      }
		    else adjust = ob - (char*)codeArray;
		  }
	      }
	/*  copy code until reloc->rpos is reached */
	    i = reloc->rpos - rpos;
	    more -= i;
	    while (--i >= 0)
		*ptr++ = getc(segFile);
	    rpos = reloc->rpos;
	/*  adjust the word according to the reloc command */
	    if (reloc->rdisp)
		adjust -= rpos;
	    else
		adjust += (int)codeArray;
	    switch (reloc->rsize)
	      {
	      case RLONG:
		*(long*)ptr = GetSigned4(segFile) + adjust;
		i = 4;
		break;
	      case RWORD:
		*(short*)ptr = GetSigned2(segFile) + adjust;
		i = 2;
		break;
	      case RBYTE:
		*(char*)ptr = GetSigned1(segFile) + adjust;
		i = 1;
		break;
	      }
	    ptr += i;
	    more -= i;
	    rpos += i;
	    nextReloc = reloc->next;
	    free(reloc);
	    reloc = nextReloc;
	  }
	for (i = more; --i >= 0; )
		*ptr++ = getc(segFile);
      }

 /* free symbol table */
    for (sym = cf->firstSymbol; sym; )
      { struct ExtSymbol *next = sym->next;
	free(sym);
	sym = next;
      }

    FreeCompilerTemp(cf, 0);
    free(cf);
    if (badLabels > 0)
      {
	fprintf(stderr, "Found %d undefined label(s)\n", badLabels);
	if (GetConfirmation(
	    "Do you want to risk executing anyway? ", stdin) == 0)
	    return BadCompileForEval;
      }
    ptr = codeArray+startLabel.offset; /* a5 */
    if (LogCode > 0)
      {
	i = cf->segSize[TextSegment];
	fprintf(LogFile,
	    "[Generated %d=#%X bytes, starting at #%X, entrypoint at #%X]\n",
	    i, i, codeArray, ptr);
      }
    return ptr;
  }
#endif

#if 0
EvalFixup(ex, module)
    struct Block *ex; struct Module *module;
  {
    Object *globalsPtr;
    register struct Declaration *decl;
    extern Object *TempGlobals;
    if (TempGlobals == NULL) return;
    for (decl = ex->decls.first, globalsPtr = TempGlobals; decl != NULL;
      decl = decl->next)
      { long addr = (long)*globalsPtr++;
	if (decl->is_proc())
	    continue;
	SetDeclToken(decl, module->name, ex->decls.first); /* NOTE Redo of OptMod! */
	if (decl->token.kind!=ConstToken || decl->token.u.constant.label==NULL)
	    fprintf(stderr,"CompileAndEval: Bad declaration: %s\n",decl->name);
	else
	  { char *label = decl->token.u.constant.label;
	    addr -= decl->token.u.constant.offset; /* normally zero */
	    AddGlobalSymbol(label, (void*)addr);
	    if (LogCode > 0)
	      { char buf[30]; int pos;
		sprintf(buf, " = /%X", addr);
		fputs(label, LogFile); fputs(buf, LogFile);
		pos = strlen(label) + strlen(buf);
		for (pos &= ~7; pos < 32; pos += 8) fputc('\t', LogFile);
		fprintf(LogFile, "| Bind %s\n", decl->name);
	      }
	  }
      }
    if (LogVerbose && module->parseTable != QStdMacros)
	PrintMacroTab(module->parseTable);
    if (TempGlobals != NULL)
      {
/* 	free(TempGlobals); NOTE - may still exist old references from procs */
	TempGlobals = NULL;
      }
  }
#endif
