// rpinterpreter.h
// 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.
// 
//---
// Design notes:
//  - Input and output to/from the interpreter goes via variables 'a'...'z'.
//    The client code determines which variables are input and output.
//    A variable not used as in/output can be used as a local/temporary variable
//    in the expression.
//  - The operations (+ - * ...) in the expression do not clip.  Any clipping
//    of the output variables to an interval is done by the client code after
//    reading the output variable values.
//

#ifndef _RPINTERPRETER_H_
#define _RPINTERPRETER_H_

#include <stdio.h>         //FILE.


//-------------------------------------------------------------------------------------
// Class RPInterpreter.
//-------------------------------------------------------------------------------------

class RPInterpreter
{
	void * m_exprdata; //Parsed expression data. Implementation is hidden.
	void * m_vardata;  //Values of the variables. Implementation is hidden.


public:
	// 
	// Class function:
	// Return a help text about the syntax of the RP input string
	// accepted by 'scanFromString()'.
	// 
	static char const * helptxt( void )
	{
		return
		"Syntax of the Reverse Polish expression string:                              \n"
		" - Single lower-case letter 'a'..'z' NOT preceded by equals sign means PUSH  \n"
		"   the value of the variable onto stack.                                     \n"
		" - Single lower-case letter preceded by equals sign \"=a\"..\"=z\" means POP \n"
		"   a value from stack and copy it into the variable.                         \n"
		" - Decimal floating-point number without sign (e.g. 2, 0.25, .5) means PUSH  \n"
		"   this nonnegative constant value onto stack.                               \n"
		" - Operators: There are only binary operators, namely                        \n"
		"      '+' (add)                                                              \n"
		"      '-' (subtract, \"ab-\" means a - b)                                    \n"
		"      '*' (multiply).                                                        \n"
		"      '/' (divide, \"ab/\" means a / b)                                      \n"
		"      '^' (exponentiate, \"ab^\" means a ^ b)                                \n"
		"   Each of these POPS two values from stack, computes, and PUSHES the result.\n"
		" - At character ';' the interpreter checks whether the stack is empty at     \n"
		"   that point, and generates an error if not.                                \n"
		" - Space characters are allowed in the input string between lexical elements \n"
		"   (but tab or newline characters are not allowed).                          \n"
		"";
	}



	// Print contents of parsed expression to FILE.
	void dump( FILE * oF ) const;



	// Initializes parsed expression to empty, and variable values to 0.0.
	RPInterpreter( void );

	~RPInterpreter( void );



	// Clear parsed expression to empty and clear all variables to 0.0.
	void reinit( void );

	// Clear the values of all variables to 0.0.
	void clearVars( void );



	//
	// Set and read the value of a variable.
	// The variable is identified by the parameter iC which is one lower-case ASCII
	// character 'a'-'z'.  (An 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 getVar( char iC , double * oValue ) const;
	void putVar( char iC, double 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 (an assertion fails if this assumption is violated).
	// 
	// On error, prints to stderr and returns false.
	// 
	bool scanFromString( char const * iStr );


	//
	// 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).  An 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 evaluate( void );

};


#endif //_RPINTERPRETER_H_