// rpinterpreter.cpp
// Copyright (c) Menno Rubingh 2016.  Web: [http://rubinghsoftware.de]
//
// MR Mar 2016.
//
// Simple general-purpose interpreter for Reverse Polish expression for 
// floating-point arithmetic.
// Scans RP expression from input string to an internal representation, 
// and evaluates this later on variables that are set (prior to evaluation) 
// and read (after evaluation) by the client code.
//
//
//---
// Uses:
//
//   llgp_between.h         -- Macros BETWEEN_IE(), BETWEEN_II().
//
//   rpimpl_ex.h            -- SContext, Ex (internal scancontext and exception).
//   rpimpl_stack.h         -- Class StackDbl.
//   rpimpl_var.h           -- Class VarTable.
//   rpimpl_oper.h, .cpp    -- Classes OperationXxx.
//   rpimpl_oplist.h, .cpp  -- ParsedOperationList.  
//   rpimpl_scan.h, .cpp    -- Function scanRPExpression().
//   rpimpl_eval.h, .cpp    -- Function evaluateRPExpression().
//
//   rpinterpreter.h, .cpp  -- Exported class RPInterpreter.
//
//
//---
// Dependency structure: 
//
//   +---------------------------------------------+
//   |  Client code                                |
//   +---------------------------------------------+
//
//   +=============================================+
//   |  rpinterpreter.h, .cpp                      |
//   |                                             |
//   |--------------------+------------------------+
//   |  rpimpl_scan       |  rpimpl_eval           |
//   |                    |                        |
//   |--------------------+----+-------------------+
//   |  rpimpl_oplist          |                   |
//   |-------------------------+                   |
//   |  rpimpl_oper            |                   |
//   |-------------------------+                   |
//   |                                             |
//   |     ,-----------------+----------------.    |
//   |     |  rpimpl_stack   |  rpimpl_var    |    |
//   |     `-----------------+----------------'    |
//   |                                             |
//   +---------------------------------------------+
//   |  rpimpl_ex.h                                |
//   +---------------------------------------------+
//
//   +---------------------------------------------+
//   |  llgp_between.h                             |
//   +---------------------------------------------+
//
//   A block uses only the blocks strictly vertically below it.
//                        
//   Thick lines isolate:  
//        A     Block A uses only block B, but not any blocks below B.
//       ===
//        B
//
//
//---
// Build to static library 'rpinterpreter.a':
//
//   g++              -ggdb -Wall  -c rpimpl_oper.cpp
//   g++              -ggdb -Wall  -c rpimpl_oplist.cpp
//   g++  -std=c++0x  -ggdb -Wall  -c rpimpl_scan.cpp
//   g++              -ggdb -Wall  -c rpimpl_eval.cpp
//   g++              -ggdb -Wall  -c rpinterpreter.cpp
//   
//   ar rcs rpinterpreter.a  \.
//         rpimpl_oplist.o rpimpl_scan.o rpimpl_eval.o rpinterpreter.o
//
//



#include "rpimpl_ex.h"         //Ex.
#include "rpimpl_var.h"        //VarTable.
#include "rpimpl_oplist.h"     //ParsedOperationList.
#include "rpimpl_scan.h"       //scanRPExpression().
#include "rpimpl_eval.h"       //evaluateRPExpression().

#include "rpinterpreter.h"     //RPInterpreter.

#include <stdio.h>

#include <math.h>              //isnan().



//--------------------------------------------------------------------------------------
// Definition of the RPInterpreter member functions.
//--------------------------------------------------------------------------------------


// Print contents of parsed expression to FILE.
void RPInterpreter:: dump( FILE * oF ) const
{
	ParsedOperationList const * pOplist = (ParsedOperationList const *)m_exprdata;

	pOplist->dump( oF );
}



// Constructor initializes parsed expression to empty, and variable values to 0.0.
RPInterpreter:: RPInterpreter( void )
{
	m_exprdata = (void *)( new ParsedOperationList() );
	m_vardata  = (void *)( new VarTable() );
}

RPInterpreter:: ~RPInterpreter( void )
{
	ParsedOperationList * pOplist = (ParsedOperationList *)m_exprdata;
	VarTable *            pVars   = (VarTable *)m_vardata;

	delete pOplist;
	delete pVars;
}



// Clear parsed expression to empty and clear all variables to 0.0.
void RPInterpreter:: reinit( void )
{
	ParsedOperationList * pOplist = (ParsedOperationList *)m_exprdata;
	VarTable *            pVars   = (VarTable *)m_vardata;

	pOplist->reinit();
	pVars  ->clear();
}

// Clear the values of all variables to 0.0.
void RPInterpreter:: clearVars( void )
{
	VarTable * pVars  = (VarTable *)m_vardata;

	pVars->clear();
}



//
// Set and read the value of a variable.
// Variable is identified by parameter iC which is one lower-case ASCII char 'a'-'z'.
// (Assertion fails if iC is out of range.)
//
// The function 'getVar()' returns false when the value of the variable is a NaN,
// which means that a DOMAIN ERROR occurred during the evaluate() call.  It returns
// true if the variable is not a NaN (in this case the variable can still be
// + or - HUGE_VAL).  
// For more info see the text file 'rpinterpreter_doc_floatingPointErrors.txt' 
// which should be contained among the sources.
//
bool RPInterpreter:: getVar( char iC , double * oValue ) const
{
	VarTable const * pVars  = (VarTable const *)m_vardata;

	*oValue = pVars->getByName( iC );
	return !isnan(*oValue);
}
void RPInterpreter:: putVar( char iC, double iValue )
{
	VarTable * pVars  = (VarTable *)m_vardata;

	pVars->putByName( iC, iValue );
}



//
// Scan RP expression from string.
// And then, test evaluation of the expression on dummy variable values (to 
// test for expression well-formedness, i.e. no stack under/overflows, and 
// stacksize is 0 at the end and at any ';' character).
//
// The input string as a whole must consist of the RP expression only, 
// with no other characters.
//
// Converts the string into an executable representation of the expression,
// stored internally in the RPInterpreter object, which can be executed later
// by calling 'evaluate()'.
// 
// Assumes that at the time of calling, the internal parsed-expression data
// is empty (assertion fails if this assumption is violated).
// 
// On error, prints to stderr and returns false.
// 
bool RPInterpreter:: scanFromString( char const * iStr )
{
	ParsedOperationList * pOplist = (ParsedOperationList *)m_exprdata;


	// Scan ParsedOperationList from input string.

	try
	{
		scanRPExpression( iStr, pOplist );
	}
	catch ( Ex e )
	{
		fprintf( stderr,
			"Expression syntax error (col %d): %s\n", 
			1 + e.getCol(), e.getMsg() );
		return false;
	}


	// Execute test evaluation, on variables with arbitrary values.
	// This checks for stack under/overflow, stacksize zero at end and at ';'.

	VarTable testvars; //Leave at initial values all 0.0.
	try
	{
		evaluateRPExpression( pOplist, &testvars ); 
	}
	catch ( Ex e )
	{
		fprintf( stderr, "Expression not well-formed (col %d): %s\n", 
			1 + e.getCol(), e.getMsg() );
		return false;
	}

	// Check whether a DOMAIN ERROR occurred during expression evaluation for 
	// CONSTANT operand values (but not for variable operand values which is left 
	// as a run-time error).  
	// Check this by checking whether any variable is a NaN. (This gives more
	// information than the fetestexcept(FE_INVALID) method, and running time is
	// not important during expression scanning.)
	for ( int k = 0; k < VarTable::getNVars(); k++ )
	{
		double val = testvars.getByIndex(k);
		if ( isnan(val) )
		{
			fprintf( stderr, 
				"Domain error (variable '%c') during test evaluation of expression\n",
				VarTable::indexToName(k) );
			return false;
		}
	}


	return true;
}



//
// Evaluate the internally stored parsed expression for the current values of 
// the variables, overwriting variables as specified in the expresssion.
// The operations (+ - * ...) in the expression do not clip.
// 
// Assumes that the parsed expression is well-formed (as checked by scanFromString(),
// i.e. no stack under/overflows, and stacksize is 0 at the end and at any ';' 
// character).  Assertion fails if this assumption is violated.
//
// The evaluate() function does NOT check internally for floating-point errors;
// see the text file 'rpinterpreter_doc_floatingPointErrors.txt' which should be 
// contained among the sources.
// The client code can detect DOMAIN ERRORS by checking the return value of the 
// getVal() function, or alternatively by bracketing a (series of) evaluate() 
// call(s) between feclearexcept() and fetestexcept(FE_INVALID).
//
void RPInterpreter:: evaluate( void )
{
	ParsedOperationList const * pOplist = (ParsedOperationList const *)m_exprdata;
	VarTable *                  pVars   = (VarTable *)m_vardata;

	try
	{
		evaluateRPExpression( pOplist, pVars );
	}
	catch ( Ex e )  //Internal exception 'Ex' should not happen because of the checks
	{               // executed in scanFromString().
		assert( false );
	}
}