#include <types.h>
//#include <hash.h>
#include <procs.h>
#include <machines.h>
#include <debug.h>
//#include <format.h>
#include <exceptions.h>
#include <Qcompile.h>
#include "builtin-syms.h"

#ifdef __GNUC__
#define IsCmpOpCode(code) ({int __tmp__ = (code); \
	__tmp__ >= (int)CmpLss_code && __tmp__ <= (int)CmpLeq_code;})
#else
#define IsCmpOpCode(code) \
  ((int)(code) >= (int)CmpLss_code && (int)(code) <= (int)CmpLeq_code)
#endif

extern struct Type *FindExprType _TAKES((Expr *));
extern char *GetTypeName(struct Type*);
extern void CompileToLong(Expr *ex, CFile *cf);
EXTERN int TypeIsInt(Type *);
#define QFuncRegMask 0xFFFF

void IndentC(CFile *cf)
{
    streambuf *ff = cf->asm_stream().rdbuf();
    int i = cf->indentation;
    while (--i >= 0) ff->sputc(' ');
}

void CompileExprQuote(struct ExprQuote *ex, CFile *cf, struct DataToken *dest)
{
#if 1
    abort();
#else
    CompileLoadValue(ex->quotee, cf, dest);
#endif
}

void CompileValueC(Object ob, CFile *cf)
{
    DumpPointerTo(ob, cf);
}

void FixNumDumpPtr(struct Any val, CFile *cf)
{
    cf->asm_stream() << (long)val.addr;
}

void DumpPtrTo(struct Any val, CFile *cf)
{
#if 0
  if (val.type != NULL) {
    extern Func FuncTabSearch();
    struct Function *func = (struct Function*)
	FuncTabSearch(val.type->desc, sDumpPtr);
    if (func != NULL) (*(IntMethod)func->clauses->code)(val, cf);
    else CompileValueC(val.addr, cf);
  }
  else
#endif
      DumpPointerTo((Root*)val.addr, cf);
}

extern "C" void CompileBlockC(
    struct Block *bl,
    register CFile *cf,
    int braces /* if surround by compound-statement braces */)
{
    register struct Statement *st;
    register struct Declaration *decl;
    int kind = 0, size;
    struct Type *returnType = NULL;
    struct Block *saveBlock = cf->curBlock;
 /* kind:  0 - no variables; 1 - use stack: return val; 2 - return block */
    if (braces) cf->asm_stream() << "({\n";
    cf->indentation += 2;
    if (bl->decls.first != NULL)
      {
	size = sizeof(StructHdr) + bl->size;
	if (bl->flags & BlockIsGlobal)
	  {
	    if (!EvalCompile(cf))
		CompileError(cf, "Bad global CompileBlock");
	  }
	else if ((bl->flags & BlockReturnSelf) == 0)
	  {
	    kind = 1;
	    for (decl = bl->decls.first; decl != NULL; decl = decl->next())
		if ( /*((struct Field*)decl)->kind == Constant_Field
		    && */ ((struct Field*)decl)->kind !=Method_Field)
		    IndentC(cf),
		    PrintTypeAsC(decl->type,
				 ((struct Field*)decl)->name,
				 cf->asm_stream()),
		    cf->asm_stream() << ";\n";
	  }
	else
	  {
#if 0
	    if (!(bl->flags & BlockGivenResult))
	      {
		returnType = ExType_Type(bl->type);
		if (returnType->name == NULL) returnType->name = GenString();
		IndentC(cf);
		cf->asm_stream() << "Struct_" << returnType->name
		    << " *" << &RESULT_sym->string() << " = StdAllocOb(");
		TypeDumpPtr(MAKE_ANY(returnType, TypeT), cf);
		cf->asm_stream() << ");\n";
	      }
#endif
	    kind = 2;
	  }
if (!(bl->flags & BlockIsGlobal))
	for (decl = bl->decls.first; decl; decl = decl->next())
#if 0
	    if (decl->size == Label_SizeCode)
	      { struct LabelExpr *label = decl->defining.label;
		MakeGenLabel(label->label, cf->generate_label_number());
	      }
	    else
#endif
		if (((struct Field*)decl)->kind == Method_Field) ;
	    else if ((decl->flags & ImplicitDeclMask) != SetDeclaration)
	      {
		IndentC(cf);
		char *nam = decl->name->string();
		cf->asm_stream() << nam << " = AllocVariable(" << nam <<");\n";
	      }
      }
    for (st = bl->first; st != NULL; st = st->next) {
	if (st->kind == MethodStatement) continue;
	if (st->kind == ForgetStatement) continue;
	IndentC(cf);
	st->src.compile(cf);
	cf->asm_stream() << ";\n";
    }
#if 0
    if (returnType != NULL) {
	IndentC(cf);
	cf->asm_stream() << "MAKE_ANY(" << RESULT_sym.string() << ", ";
	TypeDumpPtr(MAKE_ANY(returnType, TypeT), cf);
	cf->asm_stream() << ");\n";
    }
#endif
    cf->indentation -= 2;
    IndentC(cf);
    if (braces) cf->asm_stream() << "})";
}

void CompileBlock(struct Block *bl, register CFile *cf, struct DataToken *dest)
  {
#if 1
abort();
#else
    register struct Statement *st, *next;
    register struct Declaration *decl;
    int kind = 0, size;
    struct DataToken dest_token[1];
    struct Label endLabel[1];
    struct Block *saveBlock = cf->curBlock;
    extern struct DataToken *GetMemTempToken();
 /* kind:  0 - no variables; 1 - use stack: return val; 2 - return block */

/*    cf->sourcePos = bl->sourcePos; */
    AppendComment(cf, "BEGIN block");
    cf->curBlock = bl;
    bl->destToken = dest;
    if ((bl->flags & BlockHasReturn+BlockReturnSelf)
	== BlockHasReturn+BlockReturnSelf)
	CompileError(cf, "Block has both RETURN has returns self!");
    if (bl->flags & BlockHasReturn)
      {
	MakeGenLabel(endLabel, cf->generate_label_number());
	bl->endLabel = endLabel;
	if (dest == IgnoreResultToken || dest->kind == NullToken)
/* NOTE: as in CompileElse, we should use a register tmp */
	    bl->destToken = GetMemTempToken(dest_token, cf, sizeof(Object));
      }
    if (bl->flags & BlockReturnSelf) {
	MakeLabelToken(dest_token, "NoValue", 0, 0);
	bl->destToken = dest_token;
    }
    if (bl->decls.first != NULL)
      {
	size = sizeof(StructHdr) + bl->size;
	if (bl->flags & BlockIsGlobal)
	  { int i;
	    if (!EvalCompile(cf))
		CompileError(cf, "Bad global CompileBlock");
#if 0
	    for (decl = bl->decls.first, i = 0; decl; decl = decl->next, i++)
	      { char buf[40], *name; Object var;
		if (decl->is_proc())
		  {
		    SetDeclToken(decl, cf->moduleName, bl->decls.first);
		    MakePendingLabel(cf, &decl->token, decl->defining.proc);
		    continue;
		  }
		var = (Object)AllocVariable(decl->name);
		MakeLabelToken(&decl->token, NULL, var, 0);
	      }
#endif
	  }
	else if ((bl->flags & BlockReturnSelf) == 0)
	  {
	    kind = 1;
	    for (decl = bl->decls.first; decl; decl = decl->next)
		if (!decl->is_const())
		    MakeNullToken(&decl->token);
	  }
	else
	  {
	    if (bl->flags & BlockGivenResult)
		CopyToken(dest, dest_token, cf);
	    else
	      {
		MakeNullToken(dest_token);
		CompileLoadValue(bl->type, cf, PushStackToken);
		CallExternC(cf, "StdAllocOb", 0, 1, dest_token);
	      }
	    kind = 2;
	    for (decl = bl->decls.first; decl; decl = decl->next)
	      {
		if (decl->is_const() || decl->is_proc())
		   { /* MakeLabelToken done in IdTraverseAndTest */ }
		else if (decl->offset == -1)
		    MakeNullToken(&decl->token);
		else if (DeclIsInclude(decl)) {
		    struct Declaration *dcl = (struct Declaration*)decl->name;
		    for (;dcl != NULL; dcl = dcl->next)
			if (dcl->offset != -1)
			    MakeGenOffsetToken(&dcl->token,
				dest_token, decl->offset + dcl->offset);
		}
		else
		    MakeGenOffsetToken(&decl->token, dest_token, decl->offset);
		if (decl->size > 0) decl->token.options |= LiteralToken;
	      }
	  }
if (!(bl->flags & BlockIsGlobal))
	for (decl = bl->decls.first; decl; decl = decl->next)
	    if (decl->size == Label_SizeCode)
	      { struct LabelExpr *label = decl->defining.label;
		MakeGenLabel(label->label, generate_label_number());
	      }
	    else if (((struct Field*)decl)->kind == Method_Field) ;
	    else if ((decl->flags & ImplicitDeclMask) != SetDeclaration)
	      {
		CompileLoadValue(decl->name, cf, PushStackToken);
		CallExternC(cf, "AllocVariable", NULL, 1, &decl->token);
	      }
      }

    for (st = bl->first; st != NULL; st = next)
      { Expr *src;
	if (HasHType(st, Statement))
	  {
	    next = st->next;
	    src = st->src.E;
	    if (st->kind == ForgetStatement) continue;
	  }
	else
	  {
	    CompileError(cf, "Bad last Statement of Block");
	    next = NULL;
	    src = (Expr*)st;
	  }
	if (next != NULL || (bl->flags & BlockReturnSelf))
	  {
#if 1
	    CompileExpr(src, cf, IgnoreResultToken);
#else
	    struct DataToken DEC_token[1];
	    MakeNullToken(val_token);
	    CompileExpr(src, cf, val_token);
	    AddressLabelToken(DEC_token, cf, "_DEC_a5_", NULL);
	    MoveToReg(cf, val_token, ArgReg(cf));
	    CompCallC(cf, DEC_token, -1, NULL);
	    FreeToken(val_token, cf);
#endif
	  }
	else
	    CompileExpr(src, cf, bl->destToken);
      }
    AppendComment(cf, "END block");
    cf->curBlock = saveBlock;
/*?*/    if (!(bl->flags & BlockIsGlobal))
	for (decl = bl->decls.first; decl != NULL; decl = decl->next)
	    if (((struct Field*)decl)->kind != Method_Field)
		FreeToken(&decl->token, cf);
#if 0
    if (bl->flags & BlockReturnSelf)
      {
	CompileMove(cf, dest_token, bl->destToken);
/*	FreeToken(dest_token, cf); */
      }
#endif
    if (bl->flags & BlockHasReturn)
	CompLab(cf, endLabel); /* should be before last Move */
    if (bl->destToken != dest)
	CompileMove(cf, bl->destToken, dest);
#endif
  }

void CompileIdentifierC(struct Identifier *id, CFile *cf)
{
    extern struct Declaration LookupSpecialDecl[1];
    struct Declaration *decl;
    decl = Ident2Decl(id);
 /* This is also used for LoopCons_code if in an "Explicit" context */
    if (decl == NULL) abort();
    if (decl == LookupSpecialDecl) {
	CompileError(cf,
	    "W undefined identifer %s taken to be dynamic",
	    id->name->string());
	cf->asm_stream() << "LookupSpecial(";
	CompileValueC(id->name, cf);
	cf->asm_stream() << ")";
    }
    else if (decl->kind == Method_Field) {
	if (decl->blockLevel != 0) {
	    CompileError(cf,"W Not implemented: Compilation of nested function");
	    cf->asm_stream() << "new GFunction(";
	    FuncDumpPtr(decl->get_proc(), cf);
	    cf->asm_stream() << ")";
//	    ProcDescCPut(decl->get_proc(), cf, decl->fname()->string());
	}
	else {
	    Root* val = decl->name->sym_function();
	    if (val == NULL)
		CompileError(cf, "Confusion compiling function");
	    else
		CompileValueC(val, cf);
	}
    }
    else if (decl->is_const())
	CompileValueC(decl->get_const(), cf);
    else if (decl->blockLevel == ENVIR_LEVEL(cf->curProc))
#if 1
	cf->asm_stream() << decl->fname()->string();
#else
	if (cf->curProc->context->name == sSelf)
	    cf->asm_stream() << SymbolString(sSelf)
		<< "->" << SymbolString(((struct Field*)decl)->name);
	else
	    cf->asm_stream() << "((struct "
		<< ExType_Type(cf->curProc->closure->type)->class_name
		    << "*)" << SymbolString(sSELF) << ".addr)->"
			<< SymbolString(((struct Field*)decl)->name);
    else if ((decl->flags & PrivateDeclaration+NonLocalDeclaration)
      != PrivateDeclaration)
	cf->asm_stream() << SymbolString(&RESULT_sym)
	    << "->" << SymbolString(((struct Field*)decl)->name);
#endif
    else
	cf->asm_stream() << decl->fname()->string();
}

#if 0
CompileIdentifier(id, cf, dest)
    struct Identifier *id; CFile *cf; struct DataToken *dest;
  { struct Declaration *decl;
#if 1
abort();
#else
    struct DataToken src_token[1];
    AppendComment(cf,
    id->name ? (char*)SymbolString(id->name) : "internal-identifier");
    decl = Ident2Decl(id);
    if (decl == NULL) return CompileSymbol(id->name, cf, dest);
    if (id->flags & IdentFree)
      {
	if (id->decl->token.kind == NullToken)
	    CompileError(cf, "Bad CompileIdentifier");
	else
	  {
	    CompileMove(cf, &decl->token, dest);
	    MakeNullToken(&decl->token);
	  }
	return;
      }

#if 0
    if (decl->flags & NonLocalDeclaration)
	CopyIndexed(cf, cf->CP_token, decl->token.u.regoffset.offset, dest);
    else
#endif
    if (decl->is_proc())
	/* NOTE: must also set up context!!! */
	CompileLoadValue(decl->type, cf, dest);
    else if (decl->is_const()) {
	CompileLoadValue(ExType_Ptr(decl->type), cf, dest);
    }
    else if (decl->token.kind == NullToken)
      {
	    MakeRegOffsetToken(src_token, FrPtrReg(cf), decl->offset);
	    CompileMove(cf, src_token, dest);
      }
    else
	CompileCopy(cf, &decl->token, dest);
#endif
 }

CompileExprCall(struct ExprCall *ex, CFile *cf, struct DataToken *dest)
  { /* compile a call to a C procedure */
    int i; struct DataToken addr[1];
    int CProc = ex->kind <= 2;
    struct DataToken *arg_tokens, *cur_arg;
/*    cf->sourcePos = ex->sourcePos; */
    if (ex->kind == 2)
      {
#if 1
	CompileError(cf, "CompileExprCall - obsolete kind == 2");
#else
	struct Declaration *decl = (struct Declaration*)ex->proc.P;
	Object proc;
	if ((proc = decl->block) == NULL) 
	proc = LookupGlobalLabel(SymbolString(decl->name), NULL);
	AddressLabelToken(addr, cf, decl->name, NULL);
#endif
      }
    else if (ex->kind != 0 && ex->kind != 3 && ex->kind != 5)
      {
	MakeNullToken(addr);
	CompileExpr(ex->proc, cf, addr);
      }
    else
	AddressLabelToken(addr, cf, ex->proc, 0);
    arg_tokens = (struct DataToken *)calloc(ex->args, sizeof(struct DataToken));
    for (i = 0, cur_arg = arg_tokens; i < ex->args; i++, cur_arg++)
      {
	MakeNullToken(cur_arg);
	CompileExpr(ex->arg[i], cf, cur_arg);
      }
    if (CProc)
      {
	for (i = ex->args; --i >= 0;) /* push parms in reverse order */
	    CompileMove(cf, &arg_tokens[i], PushStackToken);
	CompCallC(cf, addr, ex->args, dest);
      }
    else if (ex->kind >= 5)
      { /* pass parameters in registers */
	RegSet argRegSet = 0;
	int regs[3];
	regs[0] = LeftReg(cf);
	regs[1] = RightReg(cf);
	regs[2] = AuxReg(cf);
	for (i = 0; i < ex->args; i++)
	  {
	    MoveToReg(cf, &arg_tokens[i], regs[i]);
	    argRegSet |= RegAsSet(regs[i]);
	    FreeToken(&arg_tokens[i], cf);
	  }
	cf->availRegs &= ~argRegSet;
	CompCall(cf, addr, QFuncRegMask, -1, NULL);
	if (argRegSet == 0) /* NOTE: kludge for use with MOVE macro */
	    argRegSet = cf->machineDesc->workRegs;
	cf->availRegs |= argRegSet;
	GetRegToken(addr, LeftReg(cf), cf); /* get result */
	CompileMove(cf, addr, dest);
      }
    else
      {
	for (i = 0, cur_arg = arg_tokens; i < ex->args; i++, cur_arg++)
	    CompileMove(cf, cur_arg, PushStackToken);
	CompCall(cf, addr, QFuncRegMask, -1, dest);
      }
    free(arg_tokens);
  }
#endif

#if 0
CompileExprCallC(struct ExprCall *ex, CFile *cf)
  { /* compile a call to a C procedure */
    int i; struct DataToken addr[1];
    FILE *ff = cf->asmFile;
    switch (ex->kind) {
      case 0: case 3: case 5:
	fprintf(ff, "CALL%dF(%s", ex->args, SymbolString(ex->proc.name));
	break;
      case 1: case 4: case 6:
	fprintf(ff, "CALL%d(", ex->args);
	CompileExprC(ex->proc, cf, addr);
	break;
      case 7:
	fprintf(ff, "CALL%dA(%s", ex->args, SymbolString(ex->proc.name));
	break;
      case 9:
	fprintf(ff, "({ struct Any __args__[%d] = {\n", ex->args);
	IndentC(cf);
	for (i = 0; i < ex->args; i++) {
	    if (i > 0) fprintf(ff, ", ");
	    CompileToAny(ex->arg[i], cf);
	}
	fprintf(ff, "};\n"); IndentC(cf);
	fprintf(ff, "CallFunctionWithArgs(FCALL(%s), 0, 0, __args__); })\n",
	   SymbolString(ex->proc.fcall->func->fname));
	return;
      case 10:
      {
#if 1
	struct Function *func = ex->proc.fcall->func;
	cf->aux_stream() << "extern ";
	PrintTypeAsC(func->clauses[ex->offset].expr->expr->type, NULL,
	    cf->aux_stream());
	cf->aux_stream() << " " << SymbolString(func->fname) << "_"
	    << ex->offset << "();\n";
	fprintf(ff, "%s_%d", SymbolString(func->fname), ex->offset);
#else
	CompileValueC(ex->proc.fcall->func->clauses[ex->offset].code, cf);
#endif
	fprintf(ff, "(");
	for (i = 0; i < ex->args; i++) {
	    if (i > 0) fprintf(ff, ", ");
	    ex->arg[i]->compile(cf);
	}
	fprintf(ff, ")");
	return;
      }
      default:
	CompileError(cf, "CompileExprCallC - bad kind == %d", ex->kind);
    }

    for (i = 0; i < ex->args; i++)
      {
	fprintf(ff, ", ");
	if (ex->kind >= 7)
	    CompileToAny(ex->arg[i], cf);
	else
	    CompileExprC(ex->arg[i], cf);
      }
    fprintf(ff, ")");
  }
#endif

void CompSelect(struct SelectExpr *self, register CFile *cf, struct DataToken *dest)
{
    CompileError(cf, "CompSelect called but obsolete");
}

void UnifyExpr::compile(CFile *cf)
{
    switch (set) {
      case 0:
	cf->asm_stream() << "DoEquals(";
	left.compile(cf);
	cf->asm_stream() << ", ";
	right.compile(cf);
	cf->asm_stream() << ")";
        break;
      case 1:
	cf->asm_stream() << "((";
	left.compile(cf);
	cf->asm_stream() << ") = (";
	right.compile(cf);
	cf->asm_stream() << "))";
	break;
      case 2:
	cf->asm_stream() << "Becomes(";
	left.compile(cf);
	cf->asm_stream() << ", ";
	right.compile(cf);
	cf->asm_stream() << ")";
        break;
      case 3:
        // Identity - NOTE: should of course be inlined.
	cf->asm_stream() << "Identical(";
	left.compile(cf);
	cf->asm_stream() << ", ";
	right.compile(cf);
	cf->asm_stream() << ")";
        break;
    }
}
#if 0
CompileUnify(expr, cf, dest)
    struct UnifyExpr *expr; CFile *cf; struct DataToken *dest;
  { Expr_Ptr expr_left = expr->left;
    struct DataToken tmp_token[1];
    if (expr->set && ExprCodeOf(expr_left.E) == Identifier_code)
     {
	struct Declaration *decl = Ident2Decl(expr_left.ident);
	  {
	    if (decl->is_proc())
	      {
#if 0
		if ((cf->curBlock->flags & BlockReturnSelf) == 0
		  && cf->curBlock->level > 0) /* redundant? */
		    CompileExpr(expr->right, cf, &decl->token);
		if (expr->right.code() != ProcExpr_code
		  || decl->defining.proc != expr->right.proc)
		    CompileError(cf, "CompileUnify: bad proc declaration");
		else CompileLoadValue(
#endif
	      }
	    else if (decl->kind == Pointer_Field)
		CompileExpr(expr->right, cf, &decl->token);
	    else if (decl->size == Unknown_SizeCode)
		CompileError(cf, "CompileUnify: decl %s has unknown size",
		    decl->name);
	    else if (decl->is_const()) { }
	    else if (IsBitField(decl)) {
		CompileLoadInt(cf, BitSize(decl));
		CompileLoadInt(cf, decl->offset);
		CompileCopy(cf, cf->curBlock->destToken, PushStackToken);
		CompileExpr(expr->right, cf, PushStackToken);
		CallExternC(cf, "SetBitField", 0, 4, NULL);
	    }
	    else if (decl->type.ptr != 0)
	      {
		MakeNullToken(tmp_token);
		CompileExpr(expr->right, cf, tmp_token);
		CompileLoadValue(decl->type, cf, PushStackToken);
		CompileCopy(cf, &decl->token, PushStackToken); /*dst==b2*/
		CompileMove(cf, tmp_token, PushStackToken); /*src==b1*/
		CallExternC(cf, "CopyStruct", 0, 3, NULL);
	      }
	    else
	      {
		MakeNullToken(tmp_token);
		CompileExpr(expr->right, cf, tmp_token);
		CompileLoadInt(cf, decl->size);
		CompileCopy(cf, &decl->token, PushStackToken); /*dst==b2*/
		CompileMove(cf, tmp_token, PushStackToken); /*src==b1*/
		CallExternC(cf, "bcopy", 0, 3, NULL);
	      }
	    if (dest != NULL)
		if (decl->size == 0)
		    CompileCopy(cf, &decl->token, dest);
		else
		    CompileError(cf, "CompileUnify confusion");
	    return;
	  }
      }
  }

CompileInverse(expr, cf, dest)
    struct InverseExpr *expr; CFile *cf; struct DataToken *dest;
  { struct DataToken tmp_token[1];
    MakeNullToken(tmp_token);
    CompileExpr(expr->arg.E, cf, tmp_token);
    CompileExpr(expr->func.E, cf, PushStackToken);
    CompileMove(cf, tmp_token, PushStackToken);
    CallExternC(cf, "MakeInverse", NULL, 2, dest);
  }

CompileLabelExpr(label, cf, dest)
    struct LabelExpr *label; CFile *cf; struct DataToken *dest;
  { struct DataToken tmp_token[1];
    MakeNullToken(tmp_token);
    CompileExpr(label->leftExpr.E, cf, tmp_token);
    MoveToReg(cf, tmp_token, ArgReg(cf));
    CompLab(cf, label->label);
    CompileMove(cf, tmp_token, dest);
  }

CompileGoto(jump, cf, dest)
    struct GotoExpr *jump; CFile *cf; struct DataToken *dest;
  {
    struct DataToken src_token[1];
    if (dest != IgnoreResultToken)
	CompileError(cf,
	    "Warning: Result of jump %s used for something!", jump->label);
    if (jump->result.E == NULL)
      {
	MakeLabelToken(src_token, "NoValue", 0, 0);
	MoveToReg(cf, src_token, ArgReg(cf));
      }
    else
      {
	GetRegToken(src_token, ArgReg(cf), cf);
	CompileExpr(jump->result, cf, src_token);
      }
    CompGoto(cf, jump->label->label);
    CompileMove(cf, src_token, dest);
  }

CompileReturn(ret, cf, dest)
    struct ReturnExpr *ret;
  {
    if (dest != IgnoreResultToken)
	CompileError(cf, "Warning: Result of RETURN used for something!");
    CompileExpr(ret->result.E, cf, ret->block->destToken); /* ??? */
    CompGoto(cf, ret->block->endLabel);
  }

extern int CompileExtract(), CompileProcExpr(), CompileElseExpr(),
    CompileRegExpr(), CompileMoveExpr(), CompileTypeDef();
#endif

void CompileExpr(
    register Expr *ex, /* LeftReg */
    register CFile *cf, /* RightReg */
    register struct DataToken *dest) /* AuxReg */
  {
#if 1
abort();
#else
    struct Location savePos;
    int (*f)() = NULL;

    savePos = cf->sourcePos;
    if (ex->any.sourcePos.lineNo != 0)
      {
	cf->sourcePos = ex->any.sourcePos;
	if ((unsigned)cf->sourcePos.lineNo > 2000)
	  {
	    CompileErrpr(cf, "Bad line number #%x in expr #%x, type %s",
		cf->sourcePos.lineNo, ex, ExprCodeOf(ex));
	  }
	if (savePos.lineNo != cf->sourcePos.lineNo && cf->asmFile != NULL)
	  { char buf[20];
	    sprintf(buf, "LINE %d", cf->sourcePos.lineNo);
	    AppendComment(cf, buf);
	  }
      }

    switch ((enum ExprCode)ex->any.sourcePos.code) {
      case Identifier_code: CompileIdentifier(ex, cf, dest); break;
      case ExprNode_code: CompileExprNode(ex, cf, dest); break;
      case ExprCall_code: CompileExprCall(ex, cf, dest); break;
      case ExtractExpr_code: CompileExtract(ex, cf, dest); break;
      case ProcExpr_code: CompileProcExpr(ex, cf, dest); break;
      case Block_code: CompileBlock(ex, cf, dest); break;
      case ElseExpr_code: CompileElseExpr(ex, cf, dest); break;
      case UnifyExpr_code: CompileUnify(ex, cf, dest); break;
      case InverseExpr_code: CompileInverse(ex, cf, dest); break;
      case ExprQuote_code:
	CompileLoadValue(ex->quote.quotee, cf, dest);
	break;
      case Dummy_code:
	CompileLoadValue(s_, cf, PushStackToken);
	CallExternC(cf, "AllocVariable", NULL, 1, dest);
	break;
      case GotoExpr_code: CompileGoto(ex, cf, dest); break;
      case ReturnExpr_code: CompileReturn(ex, cf, dest); break;
      case LabelExpr_code: CompileLabelExpr(ex, cf, dest); break;
      case RegExpr_code: CompileRegExpr(ex, cf, dest); break;
      case MoveExpr_code: CompileMoveExpr(ex, cf, dest); break;
      case SelectExpr_code: CompSelect(ex, cf, dest); break;
      case TypeDefExpr_code: CompileTypeDef(ex, cf, dest); break;
      default:
	abort();
    }

    cf->sourcePos = savePos;
    if (LogCodeGen > 0)
	TryIndent(LogFile, -2);
#endif
  }

static int ElseExprCount = 0;

void CompileToAny(register Expr *ex, register CFile *cf)
{
#if 0
    if (ExType_Kind(ex->any.type) == ExDirectType
     && ExType_Type(ex->any.type) == AnyT) {
	CompileExprC(ex, cf);
	return;
    }
    if (ExType_Kind(ex->any.type) == ExSignedType)
	cf->asm_stream() << "Signed_TO_Any(";
    else if (ExType_Kind(ex->any.type) == ExUnsignedType)
	cf->asm_stream() << "Unsigned_TO_Any(";
    else {
	if (ExType_Kind(ex->any.type) != ExPointerType
     /*	  || ExType_Type(ex->any.type) != PtrT*/)
	    if (ExType_Kind(ex->any.type) == ExPointerType
	     && ExType_Type(ex->any.type)->class_name != NULL) {
		cf->asm_stream() << "MAKE_ANY(";
		CompileExprC(ex, cf);
		cf->asm_stream() << ", "
		    << GetTypeName(ExType_Type(ex->any.type)) << "T)";
		return;
	    }
	    else CompileError(cf, "Bad coercion to (struct Any)");
	cf->asm_stream() << "Object_TO_Any(";
    }
#endif
    CompileExprC(ex, cf);
//    cf->asm_stream() << ")";
}

void CompileStdOp(struct ExprStdOp *ex, CFile *cf, char *op, char *proc)
{
    if (ExType_Kind(ex->type) >= ExSignedType) {
	cf->asm_stream() << "(";
	CompileExprC(ex->arg[0].E, cf);
	cf->asm_stream() << " " << op << " ";
	CompileExprC(ex->arg[1].E, cf);
	cf->asm_stream() << ")";
    }
    else {
	cf->asm_stream() << proc << "(";
	CompileToAny(ex->arg[0].E, cf);
	cf->asm_stream() << ", ";
	CompileToAny(ex->arg[1].E, cf);
	cf->asm_stream() << ")";
    }
}

void CompilePlusOp(struct ExprStdOp *ex, CFile *cf)
{   CompileStdOp(ex, cf, "+", "ApplyPlus"); }
void CompileMinusOp(struct ExprStdOp *ex, CFile *cf)
{   CompileStdOp(ex, cf, "-", "EvalMinus"); }
void CompileTimesOp(struct ExprStdOp *ex, CFile *cf)
{   CompileStdOp(ex, cf, "*", "EvalTimes"); }

#if 0
CompileStdCompare(struct ExprStdOp *ex, char *proc, char *infix, CFile *cf)
{
    FILE *ff = cf->asmFile;
    if (TypeIsInt(ex->type)) {
	cf->asm_stream() << "({ long __i0__ = ";
	CompileToLong(ex->arg[0].E, cf);
	cf->asm_stream() << "; long __i1__ = ";
	CompileToLong(ex->arg[1].E, cf);
	fprintf(ff, "; __i0__%s__i1__?__i0__:FAIL})", infix);
    }
    else {
	fprintf(ff, "%s(", proc);
	CompileToAny(ex->arg[0].E, cf);
	fprintf(ff, ", ");
	CompileToAny(ex->arg[1].E, cf);
	fprintf(ff, ")");
    }
}
#endif

void CompileToLong(Expr *ex, CFile *cf)
{
    if (TypeIsInt(ex->type))
	CompileExprC(ex, cf);
    else {
	cf->asm_stream() << "AnyToLong(";
	CompileToAny(ex, cf);
	cf->asm_stream() << ')';
    }
}

#if 0
CompileLoopTake(struct ExprStdOp *ex, CFile *cf)
{
    FILE *ff = cf->asmFile;
    struct LoopConsExpr *loop = (struct LoopConsExpr*)ex->arg[0].E;
#if 1
    if (1) {
#else
    if (loop->loopBlock->flags & ExprAtMostOneResult) {
#endif
	struct Block *closure = loop->loopBlock->enclosing;
	struct Statement *st;
	static LoopHandlerCounter = 0;
	struct Type *returnType = ExType_Type(closure->type);
	fprintf(ff, "({ register int __i__, __n__ =");
	if (ex->arg[1].E == NullExpr) fputs("MAX_SIGNED", ff);
	else CompileToLong(ex->arg[1].E, cf);
	cf->indentation += 2; fputs("; DclWriteFile(_F_);\n", ff);
	if (returnType->name == NULL) returnType->name = GenString();
	IndentC(cf);
	fprintf(ff, "Struct_%s %s[1]; /* ",
	    returnType->name, SymbolString(&RESULT_sym));
	TypeDumpPtr(MAKE_ANY(returnType, TypeT), cf);
	fprintf(ff, " */\n");
	for (st = closure->first; st != NULL; st = st->next) {
	    if (st->kind == MethodStatement) continue;
	    IndentC(cf);
	    CompileExprC(st->src.E, cf);
	    fprintf(ff, ";\n");
	}
	IndentC(cf);
	LoopHandlerCounter++;
	if (ex->arg[1].E == NullExpr)
	    fprintf(ff, "InitWriteFile(_F_,64);\n");
	else
	    fprintf(ff, "InitWriteFile(_F_,__n__>512?512:__n__);\n");
	IndentC(cf);
	fprintf(ff, "for (__i__ = 0; __i__ < __n__; __i__++)\n");
	cf->indentation += 2;
	IndentC(cf);
	fprintf(ff, "IFV(_LLAB_%d)\n", LoopHandlerCounter); IndentC(cf);
	fprintf(ff, "PutWriteFile(_F_, %s_0(%s, __i__));\n",
	    loop->proc->fname, SymbolString(&RESULT_sym));
        cf->aux_stream() << "extern struct Any "
	    << loop->proc->fname << "_0();\n";
	IndentC(cf);
	fprintf(ff, "THENV ELSEV(_LLAB_%d, End_of_file) break; ENDV\n",
	    LoopHandlerCounter);
	cf->indentation -= 2; IndentC(cf);
	fprintf(ff, "FinishUpWriteFile(_F_);})");
	cf->indentation -= 2;
	return;
    }
    fputs("GARRAYTAKE(", ff);
    CompileToAny(ex->arg[0].E, cf);
    if (ex->arg[1].E == NullExpr)
	fputs(", MAX_SIGNED)", ff);
    else
	fputs(", ", ff), CompileToLong(ex->arg[1].E, cf), fputc(')', ff);
}

CompileTakeOp(struct ExprStdOp *ex, CFile *cf)
{
    if (ExprCodeOf(ex->arg[0].E) == LoopCons_code)
	CompileLoopTake(ex, cf);
    else abort();
}
#endif

void ElseExpr::compile(CFile *cf)
{
    if (kind == 0 && then.E != NULL
     && IsCmpOpCode(e1.code())
     && TypeIsInt(e1->type)
     && (e1.bin()->arg[0].E->flags & e1.bin()->arg[1].E->flags 
      & ExprCannotFail) ) {
	CompileToLong(e1.bin()->arg[0].E, cf);
	switch (e1.code()) {
	  case CmpGrt_code: cf->asm_stream() << " > "; break;
	  case CmpLss_code: cf->asm_stream() << " < "; break;
	  case CmpLeq_code: cf->asm_stream() << " <= "; break;
	  case CmpGeq_code: cf->asm_stream() << " >= "; break;
	  case CmpNeq_code: cf->asm_stream() << " != "; break;
	  case CmpEqu_code: cf->asm_stream() << " == "; break;
	  default: abort();
	}
	CompileToLong(e1.bin()->arg[1].E, cf);
	cf->asm_stream() << " ? ";
	CompileExprC(then.E, cf);
	cf->asm_stream() << " : ";
	CompileExprC(e2.E, cf);
	return;
    }
    else {
	cf->asm_stream() << (kind ? "OR_(" : then.E == NULL ? "IF1_(" : "IF_(")
	    << (int)++ElseExprCount << ", ";
/* NOTE: if types differ (see TraverseElseExpr) must do coercion */
	PrintTypeAsC(type, NULL, cf->asm_stream());
	cf->asm_stream() << ")";
	if (then.E != NULL
	 && e1.code() == Block_code)
	    CompileBlockC(e1.block(), cf, 0);
	else
	    cf->indentation += 2,
	    e1.compile(cf);
	    cf->indentation -= 2;
	if (then.E != NULL)
	    cf->asm_stream() << '\n', IndentC(cf),
	    cf->asm_stream() << "THEN_(" << ElseExprCount << ") ";
	    cf->indentation += 2,
	    then.compile(cf);
	    cf->indentation -= 2;
	cf->asm_stream() << '\n'; IndentC(cf);
	cf->asm_stream() <<
	    (kind ? "ELSE_OR_(" : then.E==NULL ? "ELSE_IF1_(" : "ELSE_(");
	cf->asm_stream() << ElseExprCount << ") ";
	cf->indentation += 2;
	e2.compile(cf);
	cf->indentation -= 2;
	cf->asm_stream() << '\n'; IndentC(cf);
	cf->asm_stream() <<
	    (kind ? "END_OR_ " : then.E==NULL ? "END_IF1_ " : "END_IF_ ");
    }
}

void ExprList::compile(CFile*cf)
{
    cf->asm_stream() << "({ Root* _args[" << length << "];\n";
    int i;
    for (i = 0; i < length; i++) {
	IndentC(cf);
	cf->asm_stream() << "_args[" << i << "] = ";
	arg[i].E->compile(cf);
	cf->asm_stream() << ";\n";
    }
    IndentC(cf);
    cf->asm_stream() << "CallEvaluated(_args[0], 0, _args+1, 0, 0, 0, "
	<< (length-1) <<", 0);\n})";
}

void CompileExprListC(struct ExprList *ex, CFile *cf, int postfix)
{
    if (postfix) abort();
    else {
	cf->asm_stream() << "PrefixApplyAll(";
    }
    CompileExprC(ex->arg[0].E, cf);
    cf->asm_stream() << ", (struct ExprList*)[...]ENV)";
}

void CompileCollect(struct ExprStdOp *ex, CFile *cf)
{
    Name lab = GenName();
    cf->asm_stream() << '\n'; IndentC(cf);
    cf->asm_stream() <<	"({ DclWriteFile(__F__); InitWriteFile(__F__,64); IFV("
	<< lab->string() << ")\n";
    cf->indentation += 2; IndentC(cf);
    cf->asm_stream() << "struct Any __val__ = ";
    CompileToAny(ex->arg[0].E, cf);
    cf->asm_stream() << ";\n"; IndentC(cf);
    cf->asm_stream() << "PutWriteFile(__F__, __val__);RAISE(Fail, 0);\n";
    cf->indentation -= 2; IndentC(cf);
    cf->asm_stream() << "THENV ELSEV(" << lab->string()
	<< ", Fail) ENDV FinishUpWriteFile(__F__);})";
}

extern "C" void
CompileCoerce(Expr *ex, ExType type, CFile *cf)
{
#if 1
    CompileExprC(ex, cf);
#else
    FILE *ff = cf->asmFile;
    if (ExType_Kind(ex->any.type) == ExType_Kind(type)
      && ExType_Ptr(ex->any.type) == ExType_Ptr(type))
	CompileExprC(ex, cf);
#if 1
    else if (TypeIsInt(type))
	CompileToLong(ex, cf);
#else
    else if (ExprCodeOf(ex) == ExprQuote_code
      && HasHType(ex->quote.quotee.addr, FixInt)
      && ExType_Kind(type) >= ExSignedType) {
	fprintf(ff, "(");
	PrintTypeAsC(type, NULL, cf->asm_stream());
	fprintf(ff, ")%d", AnyToLong(ex->quote.quotee));
    }
    else if (ExType_Kind(type) == ExDirectType
     && ExType_Type(type) == AnyT)
	CompileToAny(ex, cf);
#endif
    else {
	fprintf(ff, "COERCE(");
	ex.compile(cf);
	fprintf(ff, ", /*TO*/");
	PrintTypeAsC(type, NULL, cf->asm_stream());
	fprintf(ff, ")");
    }
#endif
}

void CompileBinary(struct ExprStdOp *ex, char *name, CFile *cf)
{
    cf->asm_stream() << "({struct Any __arg0__ = ";
    CompileToAny(ex->arg[0].E, cf);
    cf->asm_stream() << "; struct Any __arg1__ = ";
    CompileToAny(ex->arg[1].E, cf);
    cf->asm_stream() << "; " << name << "(__arg0__,__arg1__);})";
}

void Expression::compile(CFile *cf)
{
    cerr << "{Compiled expression with code==" << code() << "}\n";
    cf->asm_stream() << "<<code:" << code() << ">>";
}

void Identifier::compile(CFile *cf)
{
    CompileIdentifierC(this, cf);
}

void Block::compile(CFile *cf)
{
    CompileBlockC(this, cf, 1);
}

void MakeTupleExpr::compile(CFile *cf)
{
    cf->asm_stream() << "MakeTuple(";
    seq.compile(cf);
    cf->asm_stream() << ")";
}

void ExprQuote::compile(CFile *cf)
{
    if (this == &NULL_expr) {
	cf->asm_stream() << "((void*)0)";
	return;
    }
#if 0
    struct Any val = ex->quote.quotee;
    if ((val.type == FixNumT || val.type == IntT)
	&& ExType_Kind(ex->any.type) >= ExSignedType)
	fprintf(ff, "%d", (long)val.addr);
    else if (ExType_Kind(ex->any.type) == ExDirectType) {
	fputs("MAKE_ANY((AnyPtr)", ff);
	DumpPtrTo(ex->quote.quotee, cf);
	fputs(", (void*)", ff);
	DumpPtrTo(MAKE_ANY(ex->quote.quotee.type, TypeT), cf);
	fputs(")", ff);
    }
    else
#endif
    DumpPtrTo(quotee, cf);
}


void CompileExprC(register Expr *ex, register CFile *cf)
{
    ex->compile(cf);
}

#if 0
DumpExprTemp(Expr *ex, CFile *cf, struct Label *label)
{
    MakeGenLabel(label, cf->generate_label_number());
    DumpExpr(ex, cf, label);
}

DumpExprHeader(Expr *ex, ostream& oust, struct Label *label, char *structName)
{
    if (label->kind == GenLabelToken) outs << "static ";
    outs << "struct " << strutName << "ExprStdOp ";
    outs << *label << " = {";
put type
put sourcePos
    outs << ex->flags;
}


void
DumpExpr(Expr *ex, CFile *cf, struct Label *label)
{
    switch (ExprCodeOf(ex)) {
      case PlusOp_code:
	struct Label labs[2];
	DumpExprTemp(ex->bin.arg[0].E, cf, &lab[0]);
	DumpExprTemp(ex->bin.arg[1].E, cf, &lab[1]);
	DumpExprHeader(ex, cf->asm_stream(), label, "ExprStdOp");
	outs << ", &" << lab[0];
	outs << ", &" << lab[1];
	cf->asm_stream() << "};\n";
	break;
      default:
	CompileError(cf, "Don't know how to dump expr with code %d",
	    (int)ExprCodeOf(ex));
    }
}
#endif
