// eval5.cpp

#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>	// signal()
#include <float.h>	// FPE_... signal codes

#include "eval5.h"

// Support routines. -----------------------------------------------------------

bool proglet_t::equal (token_t a, token_t b) {
	if (!a && !b) return false;
	if (!a || !b) return true;
	return strcmp (a, b) == 0;
}

void proglet_t::error (severity_t severity, char *msg1, char *msg2) {
	if (msg2 != 0) {
		printf ("%s, '%s'\n", msg1, msg2);
	} else {
		printf ("%s\n", msg1);
	}
	throw severity;	// Raise an exception which will be caught in main().
}

// Look up a symbol.
proglet_t::symbol_t *proglet_t::FindSymbol (token_t name) {
	int   i;
	
	for (i = 0 ; i < NumSymbols && !equal (name, symbols[i].name) ; ++i)
		/* Scan through the symbol table. */ ;
		if (i < NumSymbols) {
			return &symbols[i];
		} else {
			return 0;
		}
}

proglet_t::symbol_t *proglet_t::CreateSymbol (token_t name, SymType_t type, value_t value) {
	if (NumSymbols == MaxSymbols) error (stop_e, "No room for more symbols at", name);
	// Assume that the symbol doesn't exist already.
	strcpy (symbols[NumSymbols].name, name);
	symbols[NumSymbols].type  = type;
	symbols[NumSymbols].value = value;
	++NumSymbols;
	return &symbols[NumSymbols-1];
}

proglet_t::value_t proglet_t::GetValue (token_t name, bool AllowFunctions) {
	symbol_t *sym = FindSymbol (name);
	if (sym == 0)  error (skip_e, "symbol not found", name);
	if (!AllowFunctions && sym->type == function_e) {
		error (skip_e, "not a value", name);
	}
	return sym->value;	// Constants and variables are always allowed.
}

void proglet_t::InitSymbolTable (void) {
	static const value_t	PI = 3.14159265358979323846,
							E  = 2.71828182845904523536;
	static const int		NumPredefinedFunctions = 9,
							NumPredefinedSymbols   = 5;
	static struct {
		token_t  name;
		value_t  value;
	} PredefinedFunctions[NumPredefinedFunctions] = {
		{"rand",	0.0},
		{"abs",		1.0},
		{"sin",		1.0},
		{"cos",		1.0},
		{"tan",		1.0},
		{"asin",	1.0},
		{"acos",	1.0},
		{"atan",	1.0},
		{"atan2",	2.0}
	},
	PredefinedSymbols[NumPredefinedSymbols] = {
		{"e",		E},
		{"pi",		PI},
		{"deg",		180.0/PI},
		{"rad",		PI/180.0},
		{"time",	0.0},
	};
	
	NumFunctions = NumPredefinedFunctions;
	NumSymbols = 0;		// Incremented with each symbol created.
	for (int i = 0 ; i < NumPredefinedFunctions ; ++i) {
		CreateSymbol (PredefinedFunctions[i].name, function_e, PredefinedFunctions[i].value);
	}
	for (i = 0 ; i < NumPredefinedSymbols; ++i) {
		CreateSymbol (PredefinedSymbols[i].name, variable_e, PredefinedSymbols[i].value);
	}
}


// Overloaded functions for building up the program. ---------------------------

void proglet_t::AddInstr (instr_t instr) {
	if (ProgLen >= MaxProgLen) error (stop_e, "Program too big.");
	prog[ProgLen].instr = instr;
	++ProgLen;
}

void proglet_t::AddInstr (value_t num) {
	if (ProgLen >= MaxProgLen-1) error (stop_e, "Program too big.");
	prog[ProgLen].instr = PushNum_e; // Only possibility with numeric operand.
	++ProgLen;
	prog[ProgLen].num = num;
	++ProgLen;
}

void proglet_t::AddInstr (instr_t instr, symbol_t *sym) {
	if (ProgLen >= MaxProgLen-1) error (stop_e, "Program too big.");
	prog[ProgLen].instr = instr;	// May either assign to, or push value of symbols.
	++ProgLen;
	prog[ProgLen].sym = sym;
	++ProgLen;
}

// Lexical analysis. -----------------------------------------------------------

void proglet_t::GetNextToken (void) {
	int	TokenLen;
	
	// Skip leading whitespace.
	while ((StmtIndex < StmtLen) && (strchr (" \t", statement[StmtIndex]) != 0) ){
		++StmtIndex;
	}
	if (StmtIndex >= StmtLen) {
		error (skip_e, "No more tokens to get");
	}
	token[0] = statement[StmtIndex];
	TokenLen = 1;
	++StmtIndex;
	
	TokenType = UnknownTT_e;
	if (isalpha (token[0])) {
		TokenType = identifier_e;
		// Token is an identifier.
		// Read in the remainder of the symbol name.
		while (	StmtIndex < StmtLen					// Are there any more characters?
				&& TokenLen < MaxTokenLen			// Have we room for more characters?
				&& isalnum (statement[StmtIndex])	// Do we want them?
		) {
			token[TokenLen] = statement[StmtIndex];
			++TokenLen;
			++StmtIndex;
		}
		if (StmtIndex < StmtLen						// Is another character available?
			&& TokenLen == MaxTokenLen				// Have we run out of room for it?
			&& isalnum (statement[StmtIndex])		// Did we want it?
		) {
			token[TokenLen-1] = 0;	// Terminate the partial token so that we can print it,
			error (skip_e, "Identifier too long for token buffer", token);
		}
	} else if (isdigit (token[0])) {
		TokenType = number_e;
		// Token is a number - a 'literal numerical constant'.
		--StmtIndex;	// Back up a character and read the whole number in.
		if (sscanf (&statement[StmtIndex], "%lf%n", &TokenValue, &TokenLen) == 1) {
			StmtIndex = StmtIndex + TokenLen;
		} else {
			error (skip_e, "program error - couldn't read number", &statement[StmtIndex-1]);
		}
	} else {
		// Hopefully an operator of some kind.
	}
	token[TokenLen] = 0;	// Terminate token string.
}

void proglet_t::InitLexer (char *line) {
	// Initialise lexical bits.
	statement = line;
	StmtLen = strlen (line);
	StmtIndex = 0;
	
	GetNextToken ();
}

// Syntax analysis. ------------------------------------------------------------

int proglet_t::ParseArgList (void) {
	int   NumArgs = 0;
	while (!equal (")", token)) {
		NumArgs = NumArgs + 1;
		ParseExpression ();		// and leave the result on the stack.
		if (equal (",", token)) {
			GetNextToken ();
		}
	}
	if (!equal (")", token)) {
		error (skip_e, "Unexpected token, comma expected", token);
	}
	return NumArgs;
}

void proglet_t::ParsePrimary (void) {
	token_t  name;
	
	switch (TokenType) {
	case number_e:
		AddInstr (TokenValue);
		GetNextToken ();
		break;
		
	case identifier_e:
		strcpy (name, token);
		
		GetNextToken ();
		if (equal (token, "(")) {	// Does it look like a function name?
			symbol_t *sym = FindSymbol (name);
			if (sym == 0) {
				error (skip_e, "Unrecognised function", name);
			}
			if (sym->type != function_e) {
				error (skip_e, "Not a function", name);
			}
			GetNextToken ();
			if (ParseArgList () == (int)(GetValue(name, true) + 0.1)) { // Correct number of arguments?
				if      (equal (name, "rand"))	AddInstr (rand_e);
				else if (equal (name, "abs"))	AddInstr (abs_e);
				else if (equal (name, "sin"))	AddInstr (sin_e);	// Messy, but necessary given
				else if (equal (name, "cos"))	AddInstr (cos_e);	// the simplistic way we have
				else if (equal (name, "tan"))	AddInstr (tan_e);	// declared the functions.
				else if (equal (name, "asin"))	AddInstr (asin_e);
				else if (equal (name, "acos"))	AddInstr (acos_e);
				else if (equal (name, "atan"))	AddInstr (atan_e);
				else if (equal (name, "atan2"))	AddInstr (atan2_e);
				else {
					// We've got some tables out of step.  It shouldn't be possible to get here.
					error (stop_e, "Programming error for function", name);
				}
			} else {
				error (skip_e, "Incorrect number of arguments given for", name);
			}
			GetNextToken ();	// Discard ")".
		} else {
			symbol_t *sym = FindSymbol (name);
			if (sym == 0) error (stop_e, "No such symbol to read from", name);
			AddInstr (PushSym_e, sym);
		}
		break;
		
	default:
		if (equal ("\n", token)) {
			error (skip_e, "unexpected token", "\\n");
		} else {
			error (skip_e, "unexpected token", token);
		}
		break;
	}
}

void proglet_t::ParseFactor (void) {
	int   unary_minus = false;
	
	if (equal ("+", token)) {
		// Just ignore unary pluses and they'll go away.
		GetNextToken ();
	} else if (equal ("-", token)) {
		// We have to do something about unary minuses though.
		unary_minus = true;
		GetNextToken ();
	}
	
	if (equal ("(", token)) {
		GetNextToken ();
		ParseExpression ();
		GetNextToken ();	// Discard ")"
	} else {
		ParsePrimary ();
	}
	
	if (unary_minus) AddInstr (neg_e);
}

void proglet_t::ParseTerm (void) {
	ParseFactor ();
	while (equal ("*", token) || equal ("/", token)) {
		token_t  op;
		
		strcpy (op, token);
		GetNextToken ();
		ParseFactor ();
		
		if (equal ("*", op))	AddInstr (mul_e);
		else					AddInstr (div_e);
	}
}

void proglet_t::ParseExpression (void) {
	ParseTerm ();
	while (equal ("+", token) || equal ("-", token)) {
		token_t  op;
		
		strcpy (op, token);
		GetNextToken ();
		ParseTerm ();
		
		if (equal ("+", op))	AddInstr (add_e);
		else					AddInstr (sub_e);
	}
}

void proglet_t::ParsePrint (void) {
	GetNextToken ();
	ParseExpression ();
	AddInstr (print_e);
}

void proglet_t::ParseAssignment (void) {
	token_t  name;
	symbol_t *sym;
	
	if (TokenType != identifier_e) error (skip_e, "Name expected in assignment. Found", token);
	strcpy (name, token);
	GetNextToken ();
	if (!equal ("=", token)) error (skip_e, "'=' expected in assignment.  Found", token);
	GetNextToken ();
	ParseExpression ();
	sym = FindSymbol (name);
	if (sym == 0) {
		sym = CreateSymbol (name, variable_e);
	} else {
		if (sym->type != variable_e) error (skip_e, "Attempt to assign to non-variable", name);
	}
	AddInstr (assign_e, sym);
}

bool proglet_t::ParseStatement (void) {
	bool  done = false;
	
	if (equal ("print", token)) {
		ParsePrint ();
	} else if (equal ("run", token)) {
		done = true;	// Its cheating really to 'parse' this here.
	} else {
		ParseAssignment ();
	}
	return done;
}

// Run-time support. -----------------------------------------------------------

void proglet_t::push (value_t v) {
	if (StackDepth < MaxStackDepth) {
		stack[StackDepth] = v;
		++StackDepth;
	} else {
		error (skip_e, "stack overflow");
	}
}

proglet_t::value_t proglet_t::pop (void) {
	if (StackDepth < 1) error (skip_e, "stack underflow");
	--StackDepth;
	return stack[StackDepth];
}

// Public interface. -----------------------------------------------------------

void proglet_t::reinit (void) {
	InitSymbolTable ();
	ProgLen = 0;
}

proglet_t::proglet_t () {
	prog = new program_t [MaxProgLen];
	reinit ();
}

proglet_t::~proglet_t () {
	delete[] prog;
}

bool proglet_t::parse (char *line) {
	bool  done = false;
	
	try {
		InitLexer (line);
		StackDepth = 0;
		
		done = ParseStatement ();
	}
	catch (severity_t severity) {
		switch (severity) {
			case skip_e:					break;
			case stop_e:	done = true;	break;
			default:						break;
		}
	};
	
	return done;
}

bool proglet_t::dump (void) {
	bool  success = true;
	
	for (int pc = 0 ; pc < ProgLen && success ; ++pc) {
		switch (prog[pc].instr) {
			case PushNum_e:	++pc;	printf ("push %g\n", prog[pc].num);			break;
			case PushSym_e:	++pc;	printf ("push %g\n", prog[pc].sym->value);	break;
			case neg_e:				printf ("neg\n");		break;	// Plain arithmetic operators.
			case add_e:				printf ("add\n");		break;
			case mul_e:				printf ("mul\n");		break;
			case sub_e:				printf ("sub\n");		break;
			case div_e:				printf ("div\n");		break;
			case rand_e:			printf ("rand\n");		break;	// Arithmetic functions.
			case abs_e:				printf ("abs\n");		break;
			case sin_e:				printf ("sin\n");		break;
			case cos_e:				printf ("cos\n");		break;
			case tan_e:				printf ("tan\n");		break;
			case asin_e:			printf ("asin\n");		break;
			case acos_e:			printf ("acos\n");		break;
			case atan_e:			printf ("atan\n");		break;
			case atan2_e:			printf ("atan2\n");		break;
			case print_e:			printf ("print\n");		break;	// Statements.
			case assign_e:	++pc;	printf ("assign\n");	break;
			default:
				printf ("Unknown instruction encountered in dump.");
				success = false;
				break;
		}
	}
	return success;
}

int SigCode, SubCode;	// Global variable for passing back error info from handler() to run().
void (*OldArithErrHandler)(int); // Pointer to old handler.  Restore after run().
void ArithErrHandler (int sig, int sub) {
	SigCode = sig;
	SubCode = sub; // Could choose to ignore FPE_UNDERFLOW here.
}

// Perform as many checks as possible when parsing
// so that we can avoid run-time overheads.
bool proglet_t::run (value_t t) {	// Don't call the parameter 'time' or the
	bool		success = true;		// compiler might confuse it with 'time()'.
	value_t		a, b;
	symbol_t	*sym = FindSymbol ("time");
	
	if (sym == 0) error (stop_e, "Programming error.  'Time' symbol not found.");
	sym->value = t;	// Assumes some sort of compatibility between float and value_t.
	
	SigCode = 0;	// To catch errors that raise signals.
	errno = 0;		// To catch errors that don't raise signals.
	OldArithErrHandler = signal (SIGFPE, (void (*)(int))ArithErrHandler);
	
	for (int pc = 0 ; pc < ProgLen && success ; ++pc) {
		switch (prog[pc].instr) {
			case PushNum_e:			// This instruction has an operand, so
				++pc;				// increment the program counter to point to it.
				push (prog[pc].num);
				break;
			case PushSym_e:
				++pc;
				push (prog[pc].sym->value);
				break;
				
				// Plain arithmetic operators.
			case neg_e: push (-pop ());			break;
			case add_e: push (pop () + pop ());	break;
			case mul_e: push (pop () * pop ());	break;
			case sub_e:
				b = pop ();
				a = pop ();
				push (a - b);
				break;
			case div_e:
				b = pop ();
				a = pop ();
				push (a / b);
				break;
				
				// Arithmetic functions.
			case rand_e:	push (rand ());			break;
			case abs_e:		push (fabs (pop ()));	break;
			case sin_e:		push (sin  (pop ()));	break;
			case cos_e:		push (cos  (pop ()));	break;
			case tan_e:		push (tan  (pop ()));	break;
			case asin_e:	push (asin (pop ()));	break;
			case acos_e:	push (acos (pop ()));	break;
			case atan_e:	push (atan (pop ()));	break;
			case atan2_e:
				b = pop ();
				a = pop ();
				push (atan2 (a, b));
				break;
				
				// Statements.
			case print_e:
				if (StackDepth > 1) error (skip_e, "Final stack too deep in 'print'");
				if (StackDepth < 1) error (skip_e, "No final value on stack for 'print'");
				printf ("    %g\n", pop ());	break;
			case assign_e:
				if (StackDepth > 1) error (skip_e, "Final stack too deep in 'assign'");
				if (StackDepth < 1) error (skip_e, "No final value on stack for 'assign'");
				++pc;
				prog[pc].sym->value = pop();
				break;
			default:
				printf ("Unknown instruction encountered in run.");
				success = false;
				break;
		}
		// One of these ought to catch most errors.
		if (errno != 0) {
			success = false;
			perror ("eval");
		}
		if (SigCode != 0) {
			success = false;
			switch (SigCode) {
				// VC++ doesn't seem to allow these error conditions to be distinguished.
				//case FPE_INTOVFLOW:  printf ("Interrupt on overflow.\n");         break;   // int
				//case FPE_INTDIV0:    printf ("Integer divide by zero.\n");        break;   // int
				//case FPE_INVALID:    printf ("Invalid operation.\n");             break;
				//case FPE_ZERODIVIDE: printf ("Floating point divide by zero.\n"); break;
				//case FPE_OVERFLOW:   printf ("Numeric overflow.\n");              break;
				//case FPE_UNDERFLOW:  printf ("Numeric underflow.\n");             break;
				//case FPE_INEXACT:    printf ("Precision error.\n");               break;
				//case FPE_EXPLICITGEN:printf ("Explicit SIGFPE error.\n");         break;
				//case FPE_STACKFAULT: printf ("Floating point stack overflow.\n"); break;
			case SIGFPE:	printf ("Floating point error.\n"); break;
			default:		printf ("Unrecognised signal code %d.\n", SigCode); break;
			}
		}
	}
	signal (SIGFPE, OldArithErrHandler);
	return success;
}
