/* Copyright Per Bothner 1987. Read the file Q-INFO */
#include <types.h>
#include <iostream.h>
#include <parsefile.h>
#include <debug.h>
//#include <format.h>
#include <Qcompile.h>
#include <exceptions.h>
#include <compile_proc.h>
#include <aux_format.h>
#include <traverse.h>
#include "evalprocs.h"
#include "undobind.h"
#include <morebuf.h>
#include "gfiles.h"
#ifdef news
#include <setjmp.h>
#endif
#include "modules.h"
#ifdef __GNU_LIBRARY__
#include <string.h>
#else
#include <std.h>
#endif
#if 0
struct PendingValue _DummySaveList = {0};
#define SaveListFirst _DummySaveList.next
struct PendingValue *SaveListLast = &_DummySaveList;
#endif
extern struct TraverseData DefaultTrData;
extern "C" void raw_mode(int);

#ifndef USE_EXCEPTIONS
jmp_buf main_jmp_buf;
#endif

int PrintExprFlag = 0;

/* ParseModule (should) return:
   Module containing:
   Block
   ModuleList of referenced mdoules
 */

struct Statement *
GetLastStatement(struct Block *block)
  { struct Statement dummy;
    return (struct Statement*)
	 ((char*)block->last - ((char*)&dummy.next - (char*)&dummy));
  }

#if 0
printtime(f, string)
  { long secs; int clicks; extern long GetTime();
    static long prev_secs = 0; static int prev_clicks = -1;
    static delta_sum = 0;
    secs = GetTime(&clicks);
    fprintf(f, "[%s:", string);
    if (prev_clicks != -1)
      { int delta = (secs-prev_secs)*CLICKS_PER_SEC+(clicks-prev_clicks);
	fprintf(f, "+%d", delta);
	delta_sum += delta;	    
      }
    fprintf(f, ">>%d]", delta_sum);
    prev_secs = secs; prev_clicks = clicks;
  }
void SkipName(FILE *file)
  { int len = getc(file); fseek(file, len, 1); }
int ReadName(FILE *file, char *buf, int bufSize)
{ int len = getc(file); int buf_len = len;
    if (buf_len >= bufSize) buf_len = bufSize - 1;
    fread(buf, buf_len, 1, file);
    buf[buf_len] = '\0';
    return len;
}
#endif

unsigned char CharType[128] = {
    0, 0, 0, 0, 0, 0, 0, 0,
    0, HW, VW, VW, VW, VW, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    HW, SY, 0, 0, 0, 0, SY, 0,
    0, 0, SY, SY, SY, SY, AUX, SY,
    NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM,
    NUM, NUM, MAG, SY, SY, SY, SY, SY,
    SY, UC, UC, UC, UC, UC, UC, UC,
    UC, UC, UC, UC, UC, UC, UC, UC,
    UC, UC, UC, UC, UC, UC, UC, UC,
    UC, UC, UC, 0, SY, 0, SY, AUX,
    0, LC, LC, LC, LC, LC, LC, LC,
    LC, LC, LC, LC, LC, LC, LC, LC,
    LC, LC, LC, LC, LC, LC, LC, LC,
    LC, LC, LC, 0, SY, 0, SY, 0};

#ifndef LINKER

#if 0
#define COMPARE_LABEL(name, value)\
    if ((char*)value <= addr && (char*)value + *best_offset >= addr\
	&& (char*)value != NULL) \
      {	int new_offset = (char*)addr - (char*)value; \
	if (new_offset != *best_offset || best_label == NULL \
	  || strlen(name) < strlen(best_label)) \
	  {*best_offset = new_offset; best_label = name; }}

Name
FindClosestSymbol(char *addr, int *best_offset)
  { extern HashTable *GlobalSymbols;
    Name best_label = NULL;
    if (addr != NULL)
      {
	if (GlobalSymbols == NULL) ReadSymbolTable(NULL);
	DoForEachInTab(GlobalSymbols, COMPARE_LABEL);
      }
    return best_label;
  }

int PrintAddressBase = 16;

PrintSymbolic(addr, file)
  { int best_offset = 0x1000; char buf[20];
    Name label = FindClosestSymbol(addr, &best_offset);
    if (label == NULL)
	UIntPrint(addr, file, PrintAddressBase);
    else
      {
	PrintQuotedWord(SymbolString(label), SymbolLength(label), file, 0);
	if (best_offset > 0) fputc('+', file);
	if (best_offset != 0) SIntPrint(best_offset, file, PrintAddressBase);
      }
  }
#endif

static short ReadEvalCount = 0;

#if 0
ExprSuppressPrint(Expr *expr)
{
    if (HasHType(expr, Block)) {
	if (expr->block.flags & BlockReturnSelf) return 1;
	for (;;) {
	    expr = (Expr*)GetLastStatement(expr);
	    if (!HasHType(expr, Block)) break;
	    if (expr->block.flags & BlockReturnSelf) return 0;
	}
    }
    if (HasHType(expr, UnifyExpr)) return 1;
/* should also suppress f<<e; x R y, for R in <, <=, etc. */
/* Also statement lists whose last statement is supressible */
    return 0;
}
#endif

ostream *more_stream = NULL;

void EvalCommon(Expr *exp, struct TraverseData *data,
	   struct Module *module, Root** result)
/* code common to EvalModule, EvalInteractive, and ReadEvalPrint
 - probably these 3 can be merged */
{   
    /*AddNames(exp, module);*/
    exp = (Expr*)TraverseForEval((Block*)exp, data);
    if (data->errors <= WrnMessage) {
	struct DisplayEnv *env = (struct DisplayEnv*)alloca(
	    sizeof(struct DisplayEnv)+(data->displayMax+1)*2*sizeof(void *));
	env->minLevel = 0; env->maxLevel = data->displayMax;
	env->env[0] = NULL;
	env->tryNext = MAKE_ANY(0, 0);

	if (result)
	    exp->eval(result, &RefRoot, env);
	else {
	    long save_print_lisp;
	    ostream& os = more_stream ? *more_stream : cout;
	    switch (DefaultLanguage) {
		Root *temp_result;
	      case LispLanguage:
	      case SchemeLanguage:
		save_print_lisp = print_lisp;
		print_lisp = 1;
		exp->eval(&temp_result, &RefRoot, env);
		os << *temp_result << '\n';
		print_lisp = save_print_lisp;
		break;
	      default:
		exp->eval(&os, &Text, env);
#ifdef __HAVE_COLUMN
		if (os.rdbuf()->get_column() != 0)
		    os << '\n';
#endif
	    }
	}
    }
    else if (result)
	*result = NoValue;
}

Root* EvalInteractive(Expr *exp, struct TraverseData *data, 
			   struct Module *module)
{
    Root *tmp;
    data->clear(module);
    module->kind = 5; module->temp = NULL;
    PreEvalModule(module);
    EvalCommon(exp, data, module, &tmp);
    return tmp;
}

extern "C" int GetStackPointer();
extern "C" void SetStackPointer(int);
extern "C" void notify_and_cleanup ();

extern char *Q_version;

void ReadEvalPrint()
  {
    long refCount = 0;
    struct Any SavedResult;
    struct ParseFile *ff;
    /* note: careful about using registers that are trashed by Q code! */
    Expr *exp;
#ifdef news
    int SavedStackPtr = GetStackPointer();
#endif
    int line = 0;
    ff = OpenParseInteractive();
    if (DefaultModule->nextToCompile == NULL)
      {
	DefaultModule->nextToCompile = ModulesToCompile;
	ModulesToCompile = DefaultModule;
      }
 /* force EvalModule on modules listed in header */
    PreEvalModule(DefaultModule);
    if (ReadEvalCount++ == 0)
	printf("Welcome to Q, version %s.\n", Q_version);
#ifdef __HAVE_COLUMN
    cout.rdbuf()->set_column(0);
#endif
    SavedResult.addr = NoValue;
    SavedResult.type = NULL;
    DefaultTrData.clear(DefaultModule);

#ifndef USE_EXCEPTIONS
    morebuf mbuf(cout.rdbuf(), main_jmp_buf);
#else
    morebuf mbuf(cout.rdbuf());
#endif
#if 1
    more_stream=&cout;
#else
    ostream mstream(&mbuf);
    more_stream = &mstream;
#endif

    for (;;) {
	struct BlockSave save[1];
	struct Block *block = DefaultModule->block;
	block->first = NULL;
	block->last = &block->first;
	/* should save old decls (if any) into module */
	block->decls.first = NULL;
	block->decls.last = &block->decls.first;
	TestPushBlock(save, ff);
	ff->block = block;
	notify_and_cleanup();
//	raw_mode(0);
	exp = ParseCommand(ff);
	TestPopBlock(save);
	if (ff->errors > WrnMessage)
	  { ff->errors = 0; continue; }
	if (exp == (Expr*)EOF_mark)
	    return;
#if 0
	if (&exp->block != block)
	    ParseError(ff, "ReadEvalPrint confusion: ex=#%X != block=#%X",
		       exp, block);
#endif
/*	suppress = ExprSuppressPrint(exp);*/
	if (RealHandler(_Handler_))
	    if (IsNullExpr(exp))
	      {
		SavedResult.addr = NoValue;
		SavedResult.type = NULL;
		_Raise_(Fail, NoValue); }
	    else {
#ifdef news
		SetStackPointer(SavedStackPtr);
#endif
		_Handler_ = NullHandler;
	    }
	ClearUndoCommands();
	fflush(stdout);
	_Handler_ = InteractiveHandler;
#ifndef USE_EXCEPTIONS
	if (setjmp(main_jmp_buf))
	  {
	    SavedResult.addr = NoValue;
	    SavedResult.type = NULL;
	    continue;
	  }
#endif
	if (IsNullExpr(exp))
	    { SavedResult.addr = NoValue; SavedResult.type = NULL; continue; }
/*	exp = CoerceToBlock(exp); */
/* Changes here probably also need to be done in EvalModule */
	DefaultTrData.clear(DefaultModule);
	DefaultModule->kind = 5; DefaultModule->temp = NULL;
	PreEvalModule(DefaultModule);
	/*AddNames(exp, DefaultModule);*/
	mbuf.reset_lines();
	EvalCommon(exp, &DefaultTrData, DefaultModule, NULL);
	if (RealHandler(_Handler_)) fprintf(stdout, " |");
	if (ff->terminators & ParseEOFseen) break;
      }
  }

extern void LispEvalString(char *str, int len);
extern void SchemeEvalString(char *str, int len);

void EvalString(char *str, int len)
{
    if (len == -1) len = strlen(str);
    switch (DefaultLanguage) {
      case LispLanguage:
	LispEvalString(str, len);
	break;
      case SchemeLanguage:
	SchemeEvalString(str, len);
	break;
      default:
	Expr *exp = ParseString(str, len, DefaultModule);
	if (exp == FailedParse) {
	    fprintf(stderr,
		    "[Syntax error in command line expression: -e %.*s]\n",
		    str, len);
	    return;
	}
	DefaultTrData.clear(DefaultModule);
	EvalInteractive(exp, &DefaultTrData, DefaultModule);
    }
}
#endif /* LINKER */
