/*  prsdrv.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <stream.h>
#include <string.h>
#include <stdio.h>
#include <values.h>
#include <limits.h>
#include <hrdarth.h>

#include "debug.h"
#include "hrdcmp.h"
#include "shared.h"

#include "lexhead.h"
#include "usercom.h"
#include "yacintfc.h"
#include "baseio.h"
#include "interp.h"
#include "intfc.h"
#include "doop.h"
#include "cgidbg.h"
#include "prsdrv.h"
#include "basic.h"
#include "outtok.h"
#include "stack.h"
#include "stdyc.h"
#include "remcom.h"
#include "editnet.h"
#include "shared.h"
#include "dspe_app.h"
#include "mkstr.h"
#ifdef __NT_VC__
#include <float.h>
#define MINDOUBLE DBL_MIN
#endif

// void TestAlloc(const char *msg=0);

Stack TheConditionalStack;

int NoExecute()
{
	if (State.IsError()) return 1;
	if (!TheConditionalStack.IfTop()) return 1 ;
	return 0 ;
}

static const Int32Lim = 0x7fffffff ;

static int ToInt32(int32& Dest, double& Source)
{
	if (fabs(Source) > Int32Lim) return 0;
	Dest = (int32) Source ;
	return 1 ;
}


static int DoDoubleAssign(ValueType& Res, double Source)
{
	if (Res.Value.ValBuiltInClass = DoubleConvert(Res.Type,&Source)) {
		// LogForm("DoDoubleAssign value = %d",
		//	*(int16 *) Res.Value.ValBuiltInClass) ;
		return 1 ;
	}
	return 0;
}

int check_overflow(DecType type, double val)
{
	if (val < 0) val = -val ;
	val *= NormToOneMachWord ;
	switch(type) {
case DecCxMachWord:
case DecMachWord:
		return val > MachWord::max_positive + 1.2 ;
case DecCxAccMachWord:
case DecAccMachWord:
		return val > AccMachWord::max_positive + 1.2 ;
default:
		DbgError("check_overflow","bad type");
	}
	return 0 ;
}

static double * ValueTypeAssign(ValueType& Res,ValueType& Op1)
{
	static double Return ;
	if (!IsNumeric(Res.Type) || !IsNumeric(Op1.Type)) {
		State.Error( "assignment only supported for numeric types");
		return 0;
	}
	if (IsComplex(Res.Type) || IsComplex(Op1.Type)) {
		State.Error( "complex assignment not supported");
		return 0;
	}

	// Special cases for MachWord source or destination
	Return = ConvertToDouble(Op1);
	if (!is_floating_point_simulator()) {
		// float to machine
		if (IsMachWordType(Res.Type)) {
	  		if (IsFloat(Op1.Type)) {
				State.DoingThis(
					"Converting float value to normalized MachWord type.");
				if (check_overflow(Res.Type,Return)) State.Warning(
					"overflow, value hard limited");
			} else {
				// integer to machine
				Return /= (1. + MachWord::max_positive);
			}
		} else if (IsMachWordType(Op1.Type) && IsFloat(Res.Type)) {
			State.DoingThis(
				"Converting normalized MachWord type to floating point.");
			Return /= (1. + MachWord::max_positive);
		}
	} 

	if (!IsMachWordType(Res.Type)) if (IsFloat(Op1.Type) && !IsFloat(Res.Type))
		State.Warning("floating point value assigned to int");

	if (Res.Type == DecFloat) Res.Value.ValFloat = Return ;
	else if(Res.Type == DecInt) {
		if (!ToInt32(Res.Value.ValInt,Return)) {
			State.Error("integer overflow");
			return 0;
		}
	}
	else if (!DoDoubleAssign(Res,Return)) return 0 ;
	return &Return;
}


struct ValueType * Equal(struct ValueType* Res,struct ValueType* Op1,int op)
{
	if (NoExecute()) return 0 ;
	ValueType * delete_op1 = Op1 ;
	for (;;) {
		if (State.IsError()) return 0;
		// char buf[BufSize];
		double * AssignedValue = 0 ;
		double val ;
		if (op) {
			Op1 = BinOp(Res,op,Op1) ;
			if (!Op1) break ;
			val = Op1->double_value(DecMachWord);
			AssignedValue = &val ;
			delete_op1 = 0 ;
		}
		if (!AssignedValue) {
			if (!Res || !Op1) DbgError("Equal", "NULL pointer") ;
			if (!(AssignedValue = ValueTypeAssign(*Res,*Op1))) break ;
		}
		if (!is_floating_point_simulator() && IsMachWordType(Res->Type)) {
			int32 val = (int32) Res->double_value(DecInt) ;
			char * int_value = Concatenate(" (",dec(val),")");
			const char * float_value =
				ConvertToString(Res->double_value(DecFloat));
			State.DoingThis("Assigned ", float_value, int_value );
			delete int_value ;
		} else State.DoingThis("Assigned ",
				ConvertToString(Res->double_value(DecFloat)));
		delete delete_op1 ;
		Res->ValueDefined = 1;
		return new ValueType(Res) ;
	}
	delete delete_op1 ;
	return 0;
}



ValueType * OverwriteDeclaredObject(ValueType * Obj)
{

	const char * nm ;
	if (Obj->Class == DeclBasic) {
		SimpleUserObject * TheObject = Obj->Declared.Basic;
		nm = TheObject->GetName();
		// LogOut << "After Dump\n" ;
		TheObject->Delete() ;
		delete TheObject ;
		Obj->Declared.Basic = 0 ;
	} else {
		if (Obj->Class == DecEnt || Obj->Class==DoDeclEntity) {
			UserEntity * Ent = Obj->Value.ValEnt;
			nm = Ent->GetName();
			// LogOut << "OverwriteDeclaredObject for `" << nm << "'.\n" ;
			if (!Ent->CheckSafeDelete()) {
				State.Error(
					"cannot safely delete object `",
					Ent->GetName(), "'");
				return 0 ;
			}
			if (!Ent->Delete()) {
				State.Error("object `",
					Ent->GetName(),
					"' is not deleteable") ; 
				return 0 ;
			} else {
				delete Ent ;
				Obj->Value.ValEnt = 0 ;
			}
		} else {
			OutTokens Temp(&LogOut) ;
			Obj->Dump(Temp,new ValueTypeList);
			DbgError("OverwriteDecleareObject","bad type");
		}
	}
	// LogOut << "Overwriting `" << nm << "'\n" ;
	ValueType * Return = new ValueType(DecName,
		strcpy(new char[strlen(nm)+1],nm));
	return Return ;
}

ValueType * AlreadyDeclared(ValueType * Obj)
{
	if (NoExecute()) return 0 ;
	if (State.IsInOverwrite()) return OverwriteDeclaredObject(Obj);
	int IsBasic = Obj->Class == DeclBasic ;
	if (!IsBasic && Obj->Type != DecEnt) {
		// LogForm("Class is %d", Obj->Class);
		State.Error("syntax error");
		return 0 ;
		// DbgError("AlreadyDeclared","bad Obj");
	}
	const char * name ;
	const char * typename ;
	if(IsBasic) {
		name = Obj->Declared.Basic->GetName() ;
		typename = Obj->Declared.Basic->GetTypeName() ;
	} else {
		name = Obj->Value.ValEnt->GetName() ;
		typename = Obj->Declared.EntityClass->GetClassName() ;
	}
	OutputType channel = OutputPrompt ;
    if (State.IsInteractive()) channel = OutputCppHelp ;
	*Output + channel <<  "`" << name << "' is already declared as a `"
		<< typename << "'.\n";
	const char * Name = 0 ;
	for (;;) {
		Name = CondGetName("new name or RETURN","variable already declared");
		if (!Name) return 0 ;
		if (State.IsError()) return 0 ;
		if (!DspApplication::valid_object_name(Name)) {
			*Output + InputPrompt <<  "`" << Name <<
				"' is not a legal object name.\n" ;
			*Output << DspApplication::explain_legal_object_name() <<  ".\n" ;
			continue ;	
		}
		break ;
	}
	ValueType * Ret = new ValueType(DecName,Name);
	return Ret ;
}

struct ValueType * MakeIntValue(int32 val)
{
	// cout << "MakeIntValue called\n" ;
	struct ValueType * RetVal = new ValueType(DecInt,val);
	return RetVal ;
}

struct ValueType * MakeFloatValue(double val)
{
	// cout << "MakeFloatValue called\n" ;
	struct ValueType * RetVal = new ValueType(DecFloat,val);
	return RetVal ;
}

struct ValueType * ReturnStringValue(const char *val)
{
	struct ValueType * RetVal = new ValueType(DecString,val);
	return RetVal ;
}

struct ValueType * MakeStringValue(const char *val)
{
	const char * Copy = strcpy(new char[strlen(val)+1],val);
	struct ValueType * RetVal = new ValueType(DecName,Copy);
	return RetVal ;
}


// Convert integer to double if needed
// Error for any other type
static void DoubleConv(ValueType * Op1)
{
	if (Op1->Type == DecInt) {
		double Temp = Op1->Value.ValInt ;
		Op1->Value.ValFloat = Temp ;
		Op1->Type = DecFloat ;
	} else if (Op1->Type != DecFloat) 
		DbgError("DoubleConv","Illegal type") ;
}

static int shift_check(double shft)
{
	if (shft < 0 || shft > 32) {
		State.Warning("shift amount out of range");
		return 0 ;
	}
	return (int) shft ;
}

static int32 shift_value_check(double sh)
{
	if (sh < LONG_MIN || sh > LONG_MAX) {
		State.Warning("shift operand out of range");
        return 0 ;
	}
	return (int32) sh ;
}

double binary_operation(double op1, int oper , double op2)
{
	int shift ;
	int32 shift_val ;
	switch (oper) {
case '+' :
		return op1 + op2 ;
case '-' :
		return op1 - op2 ;
case '*' :
		return op1 * op2 ;
case '/' :
		if (fabs(op2) < MINDOUBLE) {
			State.Error("divide by zero");
			return 0 ;
		}
		return op1 / op2 ;
case SHIFT_L :
		shift = shift_check(op2);
		shift_val = shift_value_check(op1);
		return shift_val << shift ;
case SHIFT_R :
		shift = shift_check(op2);
		shift_val = shift_value_check(op1);
		return shift_val >> shift ;
default:
		DbgError("binary_operation","Illegal operator");
	} 
	return 0 ;
}

static DecType expression_type(DecType typ1, DecType typ2, int oper)
{
	static type_level[DecEndOfTypes] = {
	-1, 	// DecInvalid
	100,	// DecFloat
	30,		// DecInt
	-1,		// DecName
	-1,		// DecString
	-1,		// DecComplex
	10,		// DecMachWord
	-1,		// DecCxMachWord
	20,		// DecAccMachWord
	-1,		// DecCxAccMachWord
	-1,		// DecProcedure
	-1,		// DecDspPP
	-1,		// DecParam
	-1,		// DecEnt
	 };
	if (typ1 < 0 || typ1 >=DecEndOfTypes || typ2 < 0 || typ2 > DecEndOfTypes)
		DbgError("expression_type","out of range");
	int lev1 = type_level[typ1] ;
	int lev2 = type_level[typ2] ;
	if (lev1 < 0 || lev2 < 0) DbgError("expression_type","bad type");
	if (oper == SHIFT_L || oper == SHIFT_R) return typ1 ;
	if (lev2 > lev1) return typ2 ;
	return typ1 ;
}

struct ValueType * BinOp(ValueType* Op1,int Oper,ValueType* Op2)
{
	if (NoExecute()) return 0 ;
	if (State.IsError()) return 0 ;
	if (!Op1 || !Op2) DbgError("BinOp", "NULL pointer") ;
	if (Op1->Type == DecName || Op2->Type == DecName)
		DbgError("BinOp","Name as an operand");
	if (Op1->Type == DecEnt) return BinOpEnt(Op1,Oper,Op2);
	if (Op2->Type == DecEnt) {
		State.Error("invalid second operand type");
		return 0 ;
	}
	if (Op1->Type == DecDspPP && Op2->Type == DecDspPP)
		return BinOpEnt(Op1,Oper,Op2);
	if (Op1->Type == DecDspPP || Op2->Type == DecDspPP) {
		State.Error("invalid operands") ;
		delete Op2 ;
		return Op1 ;
	}
	double op1 = Op1->double_value(Op2->Type);
	double op2 = Op2->double_value(Op1->Type);
	double result = 0. ;
	if (Op1->Type == DecInt && ( Oper == SHIFT_L || Oper == SHIFT_R)) {
		result = 0 ;
		State.Error("shift operation with non integer value to shift");
		return Op1 ;
	} else result = binary_operation(op1,Oper,op2);
	DecType exp_type = expression_type(Op1->Type,Op2->Type,Oper);
	if (exp_type != Op1->Type) if (exp_type == Op2->Type) {
		ValueType * temp = Op1 ;
		Op1 = Op2 ;
		Op2 = temp ;
	} else DbgError("BinOp","bad types");
	Op1->double_assign(result);
	delete Op2  ;
	if (!State.IsError()) {
		char buf[2] ;
		buf[0]=Oper;
		buf[1]= 0;
		const char * Buf ;
		if (Oper < 128) Buf = buf ;
		else if (Oper == SHIFT_L) Buf = "<<" ;
		else if (Oper == SHIFT_R) Buf = ">>" ;
		State.DoingThis("Did ",Buf);
	}
	return Op1 ;
}


extern struct ValueType * UnOp(int Op,ValueType* Val)
{
	if (NoExecute()) return 0 ;
	const char * OperatorType = 0 ;
	if (Op == '!' ) {
		OperatorType = "logical negation" ;
		switch (Val->Type) {
case DecFloat:
			Val->Value.ValFloat = ! Val->Value.ValFloat ;
			break ;
case DecInt:
			Val->Value.ValInt = ! Val->Value.ValInt ;
			break ;
default:
			State.Error("invalid operand for unary operator '-'");
			return 0 ;
		}
	} else {
		if (Op != '-') {
			State.Error("unsupported unary operator");
			return 0;
		}
		OperatorType = "unary minus" ;
		switch (Val->Type) {
case DecFloat:
			Val->Value.ValFloat = - Val->Value.ValFloat ;
			break ;
case DecInt:
			Val->Value.ValInt = - Val->Value.ValInt ;
			break ;
case DecComplex:
#ifndef __NT_VC__
			*((complex *) Val->Value.ValBuiltInClass) = 
				- *((complex *) Val->Value.ValBuiltInClass) ; 
#else
			((complex *) Val->Value.ValBuiltInClass)->x = 
				- ((complex *) Val->Value.ValBuiltInClass)->x ; 
			((complex *) Val->Value.ValBuiltInClass)->y = 
				- ((complex *) Val->Value.ValBuiltInClass)->y ; 
#endif
			break ;
case DecMachWord:
			*((MachWord *) Val->Value.ValBuiltInClass) = 
				- *((MachWord *) Val->Value.ValBuiltInClass) ; 
			break ;
case DecCxMachWord:
			*((CxMachWord *) Val->Value.ValBuiltInClass) = 
				-*((CxMachWord *) Val->Value.ValBuiltInClass) ; 
			break ;
case DecAccMachWord:
			*((AccMachWord *) Val->Value.ValBuiltInClass) = 
				-*((AccMachWord *) Val->Value.ValBuiltInClass); 
			break ;
case DecCxAccMachWord:
			*((CxAccMachWord *) Val->Value.ValBuiltInClass) = 
				-*((CxAccMachWord*)Val->Value.ValBuiltInClass); 
			break ;
case DecEnt:
case DecProcedure:
case DecParam:
case DecName:
case DecDspPP:
case DecString:
case DecInvalid:
default:
			State.Error("invalid operand for unary operator '-'");
			return 0 ;
		}
	}
	State.DoingThis(OperatorType);
	return Val ;
}


extern ValueType * SetArraySize(ValueType *, ValueType *)
{
	if (NoExecute()) return 0 ;
	// LogMsg("In SetArraySize");
	return 0 ;
}




extern ValueType * SelectArrayElement(ValueType * Variable,
	ValueType * ParamList)
{
	if (NoExecute()) return 0 ;
	for(;;) { // Dummy because of weak support for labels
/*
 *		LogForm("In SelectArrayElement - typese are %d %d",
 *			Variable->Type, ParamList->Type);
 */
		if (State.IsError()) break;
	
		if (ParamList->Type != DecParam) DbgError("SelectArrayElement",
			"bad parameter list");

		SimpleUserObject& VarDec = *(Variable->Declared.Basic) ;
		if (!VarDec.GetArray()) {
			State.Error("array index for variable `",
				VarDec.GetName(), "' which is not an array");
			break ;
		}
	
		ParameterList * Params = ParamList->Value.ValParam;
		if (Params->Size() > 1) State.Error(
			"only one dimensional arrays are supported");
		if (!Params->Size()) State.Error( "array dimension missing");
	
		if (!IsNumeric(Variable->Type))
			State.Error(
			"arrays are only supported for numeric types.\n",
				"`",VarDec.GetName(),"' is not numeric");
	
		if (IsComplex(Variable->Type))
			State.Error("complex arrays are not supported");
	
		if (State.IsError()) break ;
	
		// take selected value an put it (properly converted to
		// ValInt or ValFloat) in the 'Value' Field

		ValueType& Index = *(Params->Get());
		// LogForm("IndexType = %d", Index.Type);

		if (!IsNumeric(Index.Type)) {
			State.Error("invalid array index type");
			break ;
		}
		if (IsFloat(Index.Type))
			State.Warning("`double' used as an array index");
	
		double Test = ConvertToDouble(Index);
		int32 IndexValue ;
		if (!ToInt32(IndexValue,Test)) {
			State.Error("integer overflow") ;
			return 0;
		}
/*
 *		LogForm("Selection index = %d, Limit = %d",
 *			IndexValue,VarDec.GetArraySize());
 */
		
		if (IndexValue >= VarDec.GetArraySize()) {
			State.Error("index to variable `", VarDec.GetName(),
				"' is out of range");
			break ;
		}
		
		ValueType& ThisValue = *(VarDec.GetValue());

		double ElementValue = ConvertToDouble(ThisValue,IndexValue);
		if (IsFloat(ThisValue.Type)) {
			Variable->Value.ValFloat = (double) ElementValue;
			Variable->Type = DecFloat ;
		} else {
			Variable->Value.ValInt = (int32) ElementValue ;
			Variable->Type = DecInt ;
		}

	
		delete ParamList ;
		return Variable ;
	}
	delete ParamList ;
	delete Variable ;
	return 0;
}


void IllegalChar(char x)
{
	char SmallBuf[3];
	SmallBuf[0] = x ;
	SmallBuf[1] = '\'' ;
	SmallBuf[2] = '\0' ;
	State.Error("Illegal character  `",SmallBuf,"'") ;
}



// yacc error handling routine
void YaccWarning(const char * s0,const char * s1,const char *s2,
	const char * s3,const char *s4)
{
	State.Warning( s0,s1,s2,s3,s4 );
}

void yyerror(const char * s0,const char * s1,const char *s2,
	const char * s3,const char *s4)
{
	State.Error( s0,s1,s2,s3,s4 );
}

extern "C" int yywrap()
{
	State.Error("yywrap called");
	return 1 ;
}

static void network_edit_response(int is_error)
{
	PacketHeader head(PacketNetworkEdit,is_error?
		CppEnums::statement_error : CppEnums::statement_ok);
	WriteSeg->WritePacket(head);
}

void CommandMain()
{
	// LogOut << "CommandMain called\n" ;
	UserState = 1;
	State.ClearDoExit();
	while(State.DoNotExit()) {
		State.clear_warn();
		State.ClearError() ;
		State.SetInParseStart();
		// LogOut << "in yyparse\n" ;
		yyparse() ;
		// LogOut << "exit yyparse\n" ;
		State.ClearInParse();
		network_edit_response(State.IsError());
		State.ClearNetEdit();
		State.clear_warn(1);
		if (State.IsError() && State.IsVerbose())
			*Output +OutputCppHelp <<
				"Statement not completed due to errors.\n" ;
		State.ClearError();
		DspApplication::check_redisplay();
	}
}	

int Getc()
{
	static char Buf[BufSize] = "" ;
	static char * pt = Buf ;
	static int newline = 1;
	if (newline) {
		State.SetInParseRead();
		pt = GetRawBufLine(InputCppEntry) ;
		State.SetInParse();
		// LogOut << "Getc read `" << pt << "'.\n" ;
		if (strlen(pt) > BufSize -2) DbgError("Getc", "bad line length");
		strcpy(Buf,pt);
		pt = Buf ;
		newline = 0;
	}
	if (!*pt) { // restore newline deleted by GetRawBufLine
		newline = 1;
		// LogOut << "Getc: newline\n" ;
		return '\n' ;
	}
	// LogOut << "Getc:" << *pt << "\n" ;
	return *pt++;
}


void SemanticError(SemanticErrors Err)
{
	switch (Err) {
case ErrorSimpleDeclare :
	State.Error(
	"only simple declarations of the form `declare object' are supported");
	break ;

case ErrorNoArrays :
	State.Error("Arrays are not supported");
	break ;
	}
}

void IfPush(ValueType *Expression)
{
	if (Expression->Type == DecInt) TheConditionalStack.Push(
		Expression->GetIntMemberValue());
	else if (Expression->GetDoubleMemberValue())
			TheConditionalStack.Push(1);
	else TheConditionalStack.Push(0);
}

void NotConditional()
{
	TheConditionalStack.PopAll();
}

void Else()
{
	if (!TheConditionalStack.ReadSize()) {
		State.Error("missing `if' clause for `else'");
		return ;
	}
	TheConditionalStack.Negate();
}

void IfPop()
{
	if (TheConditionalStack.ReadSize()) TheConditionalStack.Pop();
}

void IfPartEnd()
{
	TheConditionalStack.ForceTrue();
}

