Click here to Skip to main content
15,896,478 members
Articles / Programming Languages / C#

An extensible math expression parser with plug-ins

Rate me:
Please Sign up or sign in to vote.
4.92/5 (147 votes)
13 Mar 2008CPOL51 min read 1.5M   29K   364  
Design and code for an extensible, maintainable, robust, and easy to use math parser.
#include "MTParserCompiler.h"
#include "MTTools.h"
#include <windows.h>	// include after all other includes to make sure unicode is defined
#include <assert.h>



MTParserCompiler::MTParserCompiler(MTRegistrarI *pRegistrar)
{	
	m_pRegistrar = pRegistrar;	
	m_pParent = NULL;
	m_pCompilerState = NULL;

	m_defCompilerStateID = addState(new MTCompilerDefState);	
	resetExpression();

}

unsigned int MTParserCompiler::addState(MTCompilerStateI *pState)
{	
	m_pStates.push_back( pState );
	return m_pStates.size()-1;
}

MTCompilerI* MTParserCompiler::spawn(MTRegistrarI *pRegistrar) throw(MTParserException)
{
	MTParserCompiler *pObj = new MTParserCompiler( pRegistrar);	
	pObj->enableAutoVarDefinition(isAutoVarDefinitionEnabled());
			
	return pObj;
}

MTParserCompiler::~MTParserCompiler()
{
	clearItemStack(m_itemStack);
	clearUsedVars();
	
	for( unsigned int t=0; t<m_pStates.size(); t++ )
	{
		delete m_pStates[t];
		m_pStates[t] = NULL;
	}

	m_pStates.clear();	
}

void MTParserCompiler::setParent( MTCompilerI *pParent )
{
	m_pParent = pParent;
}

void MTParserCompiler::clearUsedVars()
{
	// delete variable stubs
	unsigned int nbUsedVars = m_usedVariables.size();	
	for( unsigned int t=0; t<nbUsedVars; t++ )
	{		
		if( m_usedVariables[t].isOwner )
		{
			delete m_usedVariables[t].pStub;				
		}
	}	

	m_usedVariables.clear();		// reset the used variable list	
}

void MTParserCompiler::resetExpression()
{
	clearUsedVars();
	m_originalPos.clear();	
	m_originalExpr = _T("");

	m_opStack.clear();
	m_curItemTypeStack.clear();
	
	clearItemStack(m_itemStack);
	pushDummyValue();		// to avoid crashing the parser if evaluate is called
							// without an expression
}

ItemInfoStack* MTParserCompiler::getItemStack()
{
	return &m_itemStack;
}

void MTParserCompiler::enableAutoVarDefinition(bool enable)
{
	m_isAutoVarDefinitionEnabled = enable;
}

bool MTParserCompiler::isAutoVarDefinitionEnabled()const
{
	return m_isAutoVarDefinitionEnabled;
}

void MTParserCompiler::compile(const MTCHAR *expr) throw(MTParserException)
{
	
	resetExpression();	
	clearItemStack(m_itemStack);	// remove the dummy value	
	
	m_syntax = m_pRegistrar->getSyntax();
	
	m_originalExpr = expr;
		
	parseExpression(m_originalExpr);	

	// the variable puts its value directly on the val member
	unsigned int nbUsedVars = getNbUsedVars(); 
	ItemInfoStack::iterator itemIter = m_itemStack.begin();
	for( unsigned int t=0; t<nbUsedVars; t++ )
	{
		// The first real variable on the stack is the last used variable in the list
		if( m_usedVariables[nbUsedVars-1-t].isOwner )
		{
			((MTVarStub*)m_usedVariables[nbUsedVars-1-t].pStub)->setValuePtr(&itemIter->val);

			// the real variable puts its value in its value member
			itemIter->pParentItemArg = &itemIter->val;		
			itemIter++;
		}	
	}
	
	updateParentValPtr();

	// remove all evaluated items from the stack
	// since their values have already been put in their
	// parent items	(constant folding optimization)
	removeAllEvaluatedItem(m_itemStack);
	
	// allow the expression to be evaluated only if there is something
	// to be evaluated...!
	if( m_itemStack.size() > 0 )
	{
		// the last item puts its result in its own val variable
		ItemInfoStack::iterator popIter = m_itemStack.end()-1;
		popIter->pParentItemArg = &popIter->val;					
	}		
	else
	{
		// empty expression, so return a dummy result: NaN
		pushDummyValue();		
	}
}

bool MTParserCompiler::isConstant()const
{
	// if the last item is evaluated, then this is a constant expression
	return (m_itemStack.end()-1)->isEvaluated;	
}

void MTParserCompiler::onVarRedefined(MTVariableI *pVar) throw(MTParserException)
{
	// if this is a used variable, replace the variable object
	unsigned int index;
	if( findUsedVar(pVar->getSymbol(), index) )
	{
		if( m_usedVariables[index].isOwner )
		{
			// find the variable and update the pointer
			ItemInfoStack::iterator itemIter = m_itemStack.begin();
			ItemInfoStack::iterator endIter = m_itemStack.end();
			while( itemIter != endIter )
			{				
				if( lstrcmp(itemIter->pEval->getSymbol(), pVar->getSymbol())==0 )
				{
					itemIter->pEval = pVar;
					return;
				}

				itemIter++;
			}
			
			// should have found the variable...
			assert(false);
		}
		else
		{
			// can't update stack pointers... not implemented yet
			assert(false);
		}
	}
}

void MTParserCompiler::pushDummyValue()
{
	SItemInfo exprInfo;		
	exprInfo.isEvaluated = true;
	exprInfo.val = MTTools::generateNaN();	
	m_itemStack.push_back(exprInfo);

}


// push an item type 
void MTParserCompiler::pushCurItemType()
{
	SParserCurItem itemType;
	itemType.current = e_ItemType_None;
	itemType.previous = e_ItemType_None;
	m_curItemTypeStack.push_back(itemType);
}

void MTParserCompiler::setCurItemType( EItemType type )
{
	int lastID = m_curItemTypeStack.size()-1;
	m_curItemTypeStack[lastID].previous = m_curItemTypeStack[lastID].current;
	m_curItemTypeStack[lastID].current = type;
}

MTParserCompiler::EItemType MTParserCompiler::getPrevItemType()
{
	int lastID = m_curItemTypeStack.size()-1;
	return m_curItemTypeStack[lastID].previous;
}

MTParserCompiler::EItemType MTParserCompiler::getCurItemType()
{
	int lastID = m_curItemTypeStack.size()-1;
	if( lastID != -1 )
	{
		return m_curItemTypeStack[lastID].current;		
	}
	else
	{
		return e_ItemType_None;
	}
}


void MTParserCompiler::setArgValueFlag(bool val)
{
	if( m_opStack.size() > 0 )
	{
		int t=m_opStack.size()-1;

		// if the item is a bracket, assign the value to the previous item
		if( m_opStack[t].precedence == MTOPEN_BRACKET_PRECEDENCE )
		{
			t--;
		}

		if( t>=0 )
		{
			// if this ais a bracket, assign the value anyway so that
			// the value can be propagated to the previous-previous item
			m_opStack[t].argValue = val;
		}		
	}	
}

bool MTParserCompiler::incArgCountIfArgValue(std::vector<SOpStackItem> &opStack)
{
	if( opStack.size() > 0 )
	{
		int t=opStack.size()-1;

		// if the item is a bracket, assign the value to the previous item
		if( opStack[t].precedence == MTOPEN_BRACKET_PRECEDENCE )
		{
			t--;
		}

		if( t>=0  )
		{
			// increment the argument count only if this is a function
			if( opStack[t].argValue )
			{	
				if( opStack[t].itemType == e_ItemType_Function )
				{
					opStack[t].nbArgs++;				
				}

				return true;
			}	
		}
	}

	return false;	
}

void MTParserCompiler::clearItemStack(ItemInfoStack &stack)
{
	unsigned int size = stack.size();

	for( unsigned int t=0; t<size; t++)
	{
		clearItemStackItem(stack, t);		
	}	

	stack.clear();
}

inline void MTParserCompiler::clearItemStackItem(ItemInfoStack &stack, int item)
{
	if( stack[item].pArg != NULL )
	{
		delete []stack[item].pArg;		
	}

	if( stack[item].canDelete )
	{
		delete stack[item].pEval;
	}	
}

void MTParserCompiler::pushFuncArg(const MTCHAR *arg) throw(MTParserException)
{
	unsigned int itemStackSizeBef = m_itemStack.size();
	pushCompilerVars();
	m_curItemTypeStack.clear();
	m_opStack.clear();

	parseExpression(arg);
	
	popCompilerVars();

	// must not be an empty argument!
	if( m_itemStack.size() == itemStackSizeBef )
	{
		throwParsingExcep(MTPARSINGEXCEP_InvalidSyntax, 0);
	}	
}

void MTParserCompiler::pushCompilerVars()
{
	sCompilerVar s;
	s.curPos = m_curPos;
	s.curWord = m_curWord;
	s.expression = m_expression;
	s.lastWord = m_lastWord;
	s.nextChar = m_nextChar;
	s.originalPos = m_originalPos;	
	s.opStack = m_opStack;
	s.curItemTypeStack = m_curItemTypeStack;
	s.pCompilerState = m_pCompilerState;

	m_compilerVars.push_back(s);	
}

void MTParserCompiler::popCompilerVars()
{
	unsigned int lastId = m_compilerVars.size()-1;

	m_curPos = m_compilerVars[lastId].curPos;
	m_curWord = m_compilerVars[lastId].curWord;
	m_expression = m_compilerVars[lastId].expression;
	m_lastWord = m_compilerVars[lastId].lastWord;
	m_nextChar = m_compilerVars[lastId].nextChar;
	m_originalPos = m_compilerVars[lastId].originalPos;
	m_pCompilerState = m_compilerVars[lastId].pCompilerState;	
	m_opStack = m_compilerVars[lastId].opStack;
	m_curItemTypeStack = m_compilerVars[lastId].curItemTypeStack;

	m_compilerVars.erase( m_compilerVars.begin()+lastId );	
}

void MTParserCompiler::parseExpression(const MTSTRING &expr) throw(MTParserException)
{	
	// remove space characters...		
	MTTools::removeSpaces(expr, m_originalPos, m_expression);

	if( m_expression.size() == 0 )
	{
		return;		// nothing to parse!
	}		

	pushCurItemType();				// push a null item type to begin

	unsigned int length = m_expression.size();
	clearCurWord();
	
	MTCHAR curChar = 0;	
	m_nextChar = 0;	
	MTSTRING opSymbol;		
	m_curPos = 0;	

	setState(m_pStates[m_defCompilerStateID]);

	// Algorithm
	// two stacks: one for operators and functions (opStack)
	// and one for the ordered expression items (exprStack).  The last item will be
	// evaluated last.
	// We want to order each expression item (op, function and value) by evaluation precedence.	
	// The opStack is a temporary stack used during the parsing step only.	
		
	for( m_curPos=0; m_curPos<length; m_curPos++ )
	{
		curChar = m_expression[m_curPos];
		if( m_curPos<length-1 )
		{
			m_nextChar = m_expression[m_curPos+1];
		}
		else
		{
			m_nextChar = 0;
		}

		// ask the current compiler state to compile the char
		if( !m_pCompilerState->compileChar(curChar) )
		{
			// the state didn't process the char, so we take it 
			bool isOp = m_pRegistrar->areNextCharsOpString(&m_expression[m_curPos], length-m_curPos, opSymbol);		

			if( isOp )
			{
				m_pCompilerState->onOp(opSymbol);
			}
			else
			{
				if( curChar == m_syntax.argumentSeparator)
				{			
					m_pCompilerState->onArgSeparator();
				}
				else if(curChar == MTOPEN_BRACKET)
				{
					m_pCompilerState->onOpenBracket();
				}
				else if(curChar == MTCLOSE_BRACKET)
				{
					m_pCompilerState->onCloseBracket();
				}				
				else
				{
					m_curWord += curChar;
				}
			}
		}
	}		
	
	m_pCompilerState->onEndOfFormula();
	
	// parse the last word...
	parseWord();

	// destack remaining operators, beginning by the end
	unsigned int nbOps = m_opStack.size();
	unsigned int invT = nbOps - 1;
	
	for( unsigned int t=0; t<nbOps; t++, invT-- )
	{
		
		int curOpPrecedence = m_opStack[invT].precedence;
		if( curOpPrecedence == MTOPEN_BRACKET_PRECEDENCE )
		{			
			throwParsingExcep(MTPARSINGEXCEP_MissingCloseBracket, m_opStack[invT].exprStrPos);
		}		
		
		pushOnExprStack(m_opStack[invT]);
	}		
	
}

void MTParserCompiler::setState(MTCompilerStateI *pState)
{	
	m_pCompilerState = pState;
	pState->enter(this, getRegistrar());
}

void MTParserCompiler::setState(unsigned int stateID)
{
	setState(m_pStates[stateID]);
}

void MTParserCompiler::exitState()
{
	// select the default state...
	setState(m_pStates[m_defCompilerStateID]);
}

void MTParserCompiler::removeAllEvaluatedItem(ItemInfoStack &stack)
{
	unsigned int size = stack.size();

	if( size > 0 )
	{
		// we don't remove the last item
		// because this is the result
		size--;

		for( unsigned int t=0; t<size; t++ )
		{
			if( stack[t].isEvaluated )
			{
				clearItemStackItem(stack, t);
				stack.erase(stack.begin()+t);
				t--;
				size--;
			}
		}
	}
}

// add an operator or a function on the expression stack
void MTParserCompiler::pushOnExprStack(const SOpStackItem &item, bool findItem  ) throw(MTParserException)
{	
	SItemInfo info;
	
	info.pEval = item.pEval;	
	
	// if this is a function, we find a function with the specified number
	// of arguments
	if( item.itemType == e_ItemType_Function )
	{
		if( findItem )
		{
			unsigned int fID;
			if( !m_pRegistrar->findFunction(m_pRegistrar->getFunc(item.itemID)->getSymbol(), item.nbArgs, fID) )
			{					
				throwParsingExcep(MTPARSINGEXCEP_OverloadedFuncNotFound, item.exprStrPos, m_pRegistrar->getFunc(item.itemID)->getSymbol(), item.nbArgs);
			}

			// replace the function handler by this "maybe" overloaded function
			info.pEval = m_pRegistrar->getFunc(fID);
		}

		// just before using the function, do the late initialization...
		((MTFunctionI*)info.pEval)->doLateInitialization(this, getRegistrar());		
	}
	
	// allocate the memory for the function arguments...
	info.nbArgs = item.nbArgs;
	if( info.nbArgs > 0 )
	{
		info.pArg = new MTDOUBLE[info.nbArgs];
	}
	else
	{
		info.pArg = NULL;	// no argument
	}

	
	info.canDelete = item.canDelete;
	info.exprStrPos = item.exprStrPos;	
	m_itemStack.push_back(info);	
}


// parse a word
// It can be a function, a variable, a constant or a value
// if it is a function, add it on the opStack
// if it is a variable, a constant or a value, add it on the exprStack
bool MTParserCompiler::parseWord() throw(MTParserException)
{	

	bool ret = true;
	// parse the current m_curWord
	if( m_curWord.size() == 0 )
	{
		return ret;		
	}	

	
	int exprStrPos = m_curPos-m_curWord.size();

	if( getCurItemType() != e_ItemType_Operator &&
		getCurItemType() != e_ItemType_None)
	{	
		// this m_curWord must be preceded by an operator if this is
		// not the firt expression item.  For example, a function cannot come
		// after another function; there must be an operator between.
		throwParsingExcep(MTPARSINGEXCEP_MissingOp, exprStrPos, m_curWord.c_str());
	}


	// only functions are followed by an open bracket
	if( m_expression[m_curPos] == MTOPEN_BRACKET )
	{
		// it's a function
		// it can be a normal function or a function with a custom compiler
		unsigned int funcID;
		if( m_pRegistrar->isAFunction(m_curWord.c_str(), funcID) )
		{			
			// it's a normal function...
			setArgValueFlag(true);
			setCurItemType(e_ItemType_Function);			
			
			MTCompilerStateI *pCompilerState = m_pRegistrar->getFunc(funcID)->getCompilerState();
			if( pCompilerState == NULL )
			{
				// no custom compiler, so we have to compile this function 
				SOpStackItem item;
				item.exprStrPos = exprStrPos;
				item.precedence = e_MTOpPrec_FCT;				
				item.nbArgs = 0;		// will be incremented later...
				item.pEval = m_pRegistrar->getFunc(funcID);	
				item.argValue = false;
				item.itemType = e_ItemType_Function;
				item.itemID = funcID;
				m_opStack.push_back(item);
			}
			else
			{
				// let's the custom compiler do the hard job...!				
				setState(pCompilerState);
				ret = false;	// force the current state to exit
			}			
		}		
		else
		{		
			throwParsingExcep(MTPARSINGEXCEP_UndefinedFunc, exprStrPos, m_curWord.c_str());
		}	
		
	}
	// if only numerical characters -> value
	// so a variable name cannot be composed of numbers only
	else if( MTTools::isOnlyNum(m_curWord, m_syntax.decimalPoint) )
	{			
		// it's a numerical value
		MTDOUBLE val;
		strToVal(m_curWord.c_str(), &val);
		pushValue(val);		
	}
	else
	{
	
		// it could be a variable or a constant
		unsigned int constID;		
		if( m_pRegistrar->findConst(m_curWord.c_str(), constID) )
		{
			// it's a constant		
			MTSTRING cName;
			MTDOUBLE cVal;
			m_pRegistrar->getConst(constID, cName, cVal);
			pushValue(cVal);			
		}
		else
		{	
			// it's a variable
			try
			{
				pushVariable(m_curWord.c_str());			
			}
			catch( MTParserException )
			{
				throwParsingExcep(MTPARSINGEXCEP_UndefinedVar, exprStrPos, m_curWord.c_str()); 							
			}
		}
	}

	clearCurWord();	
	return ret;

}

void MTParserCompiler::pushValue(const MTDOUBLE &val)
{
	setCurItemType(e_ItemType_Value);

	int exprStrPos = m_curPos-m_curWord.size();

	// add it to the exprStack
	SItemInfo exprInfo;	
	exprInfo.exprStrPos = exprStrPos;
	exprInfo.isEvaluated = true;
	exprInfo.val = val;	
	m_itemStack.push_back(exprInfo);
	
	setArgValueFlag(true);
}

void MTParserCompiler::pushVariable(const MTCHAR *varName) throw(MTParserException)
{
	SItemInfo exprInfo;	
	exprInfo.isEvaluated = false;
	int exprStrPos = m_curPos-m_curWord.size();		
	exprInfo.pEval = getVar(varName);
	setCurItemType(e_ItemType_Value);
	exprInfo.exprStrPos = exprStrPos;
	
	// add it to the exprStack
	m_itemStack.push_back(exprInfo);
	setArgValueFlag(true);
}

MTVariableI* MTParserCompiler::registerUsedVariable(const MTCHAR *symbol) throw(MTParserException)
{
	// add this variable to the used variable list	(don't add twice the same variable)		
	unsigned int index;
	if( !findUsedVar(symbol, index) )
	{		
		// the variable is not already used so create a stub and
		// link it to the real variable object
		sUsedVar usedVar;		
		MTVarStub *pStub = new MTVarStub;
		usedVar.pStub = pStub;
		usedVar.isOwner = true;
		usedVar.symbol = symbol;
		m_usedVariables.push_back(usedVar);
		
		// push the real variable on the stack		
		SItemInfo exprInfo;		
		exprInfo.exprStrPos = 0;
		exprInfo.isEvaluated = false;				
		exprInfo.pEval = m_pRegistrar->getVarBySymbol(symbol);						
		exprInfo.ignore = true;
		m_itemStack.insert(m_itemStack.begin(), exprInfo);					

		return pStub;
	}
	else
	{
		// variable already registered
		return m_usedVariables[index].pStub;		
	}
}

MTVariableI* MTParserCompiler::getVar(const MTCHAR *symbol) throw(MTParserException)
{		
	if( !m_pRegistrar->isVarDefined(symbol) )
	{
		// this variable is not defined... if the autoDefine feature is on, define it
		bool isDef = false;
		if( isAutoVarDefinitionEnabled() )
		{
			try
			{
				m_pRegistrar->defineVar(symbol);
				isDef = true;										
			}
			catch( MTParserException )
			{						
				// do nothing: the variable cannot be defined...
				// so a parsing exception will be thrown
				isDef = false;
			}
		}
		
		if( !isDef && m_pParent != NULL )
		{
			// still not defined,
			// look in our parent...				
			MTVariableI *pVar = m_pParent->getVar(symbol);
			sUsedVar usedVar;
			usedVar.pStub = pVar;	
			usedVar.isOwner = false;
			usedVar.symbol = symbol;
			m_usedVariables.push_back(usedVar);
			return pVar;
		}
		else if( !isDef )
		{
			// variable not found
			throwParsingExcep(MTDEFEXCEP_ItemNotFound, symbol);			
		}
	}

	return registerUsedVariable(symbol);	
}

void MTParserCompiler::pushFunction(const MTCHAR *funcName, unsigned int nbArgs) throw(MTParserException)
{
	SOpStackItem item;
	
	unsigned int funcID;
	if( !m_pRegistrar->findFunction(funcName, nbArgs, funcID) )
	{
		throwParsingExcep(MTPARSINGEXCEP_OverloadedFuncNotFound, getCurPos(), funcName, nbArgs);
	}

	MTFunctionI *pFunc = m_pRegistrar->getFunc(funcID);
	item.exprStrPos = getCurPos();
	item.precedence = e_MTOpPrec_FCT;	
	item.nbArgs = nbArgs;
	item.pEval = pFunc;	
	item.argValue = false;
	item.itemType = e_ItemType_Function;
	item.itemID = funcID;	

	pushOnExprStack(item);

	setCurItemType(e_ItemType_Function);
	setArgValueFlag(true);

	
}

void MTParserCompiler::pushFunction(MTFunctionI *pFunc, unsigned int nbArgs) throw(MTParserException)
{	
	SOpStackItem item;
	item.exprStrPos = getCurPos();
	item.precedence = e_MTOpPrec_FCT;	
	item.nbArgs = nbArgs;
	item.pEval = pFunc;	
	item.canDelete = true;
	item.argValue = false;
	item.itemType = e_ItemType_Function;
	item.itemID = -1;	

	pushOnExprStack(item, false);

	setCurItemType(e_ItemType_Function);
	setArgValueFlag(true);	
}

void MTParserCompiler::addCurChar()
{
	m_curWord += m_expression[m_curPos];
}

void MTParserCompiler::clearCurWord()
{
	m_lastWord = m_curWord;
	m_curWord = _T("");	
}

const MTCHAR* MTParserCompiler::getCurWord()
{
	return m_curWord.c_str();
}

bool MTParserCompiler::findUsedVar(const MTCHAR *varName, unsigned int &index)const
{
	unsigned int size = m_usedVariables.size();
	for( unsigned int t=0; t<size; t++ )
	{
		if( lstrcmp( varName, m_usedVariables[t].symbol.c_str() ) == 0 )
		{
			index = t;
			return true;
		}
	}

	return false;
}

// convert a string value to a double value
void MTParserCompiler::strToVal(const MTCHAR *val, double *pVal)const
{
	MTCHAR **pEnd=NULL;
	
	// first replace the decimal point character by the one
	// specified by the current locale

	// ***** Todo investigation needed (potential bug)
	// for an unknown reason, the system conversion routine
	// doesn't take the decimal point character specified in the 
	// the locale settings.  So, temporarily we replace the decimal
	// point character specified in the syntax settings by the '.' character.
	MTCHAR localeDecimalPoint = MTSYSTEM_DECIMALPOINT;	// _wsetlocale(LC_NUMERIC, NULL);	

	// only replace if the two characters are different
	if( m_syntax.decimalPoint != localeDecimalPoint )
	{
		MTSTRING newVal = val;
		unsigned int length = newVal.size();
		for( unsigned int t=0; t<length; t++ )
		{
			if( newVal[t] == m_syntax.decimalPoint )
			{
				newVal[t] = localeDecimalPoint;
				break;				
			}
		}

		*pVal = MTSTRTOD(newVal.c_str(),pEnd);
	}
	else
	{
		*pVal = MTSTRTOD(val,pEnd);
	}
	
}

// Use a recursive algorithm to compute where each item puts its result.  This 
// allows an iterative algorithm to be used in the evaluation step.
void MTParserCompiler::updateParentValPtr()
{
	if( m_itemStack.size() == 0 )
	{
		return;		// empty expression or expression not set!	
	}	
	
	// recursively evaluate each item, beginning by the last (it will
	// also be the last to be evaluated)

	bool constant = true;
	evaluateValidate( m_itemStack.size()-1, constant);			
}

MTDOUBLE MTParserCompiler::evaluateValidate(int node, bool &constant) throw(MTParserException)
{
	SItemInfo *pExpr = &m_itemStack[node];
	
	if( pExpr->isEvaluated)
	{			
		return pExpr->val;
	}		
	
	bool isConstant = pExpr->pEval->isConstant();
	if( !isConstant )
	{
		constant = false;
	}

	m_popPtr = node-1;
	
	// pop arguments...
	int nbArgs = m_itemStack[node].nbArgs;	
	bool constantItem = isConstant;
	for( int t=nbArgs-1; t>=0; t-- )
	{		
		m_itemStack[node].pArg[t] = popValidate(&m_itemStack[node].pArg[t], constantItem);		
	}

	MTDOUBLE val = 0;
	
	// evaluate the item only if the item and its arguments are constants
	// else, the item will be evaluated at evaluation time.
	if( constantItem )
	{
		val = pExpr->pEval->evaluate(m_itemStack[node].nbArgs, m_itemStack[node].pArg);		
		pExpr->val = val;
		pExpr->isEvaluated = true;
	}		
	else
	{
		// we are not constant so the parent item isn't too
		constant = false;
	}
	
	return val;		
}


MTDOUBLE MTParserCompiler::popValidate(MTDOUBLE *pParentItemArg, bool &constant) throw(MTParserException)
{
	if( m_popPtr < 0 )
	{
		// not enough argument
		throwParsingExcep(MTPARSINGEXCEP_InternalError, -1);
	}

	MTDOUBLE val;
	SItemInfo *pExpr = &m_itemStack[m_popPtr];

	if( !pExpr->ignore )
	{		
		pExpr->pParentItemArg = pParentItemArg;

		if( pExpr->isEvaluated )
		{
			val = pExpr->val;	
			m_popPtr--;				
		}
		else
		{
			val = evaluateValidate(m_popPtr, constant);	
		}	

		return val;
	}
	else
	{
		// skip this item
		m_popPtr--;				
		return popValidate(pParentItemArg, constant);
	}
}

unsigned int MTParserCompiler::getNbUsedVars()const
{
	return m_usedVariables.size();
}

MTSTRING MTParserCompiler::getUsedVar(unsigned int varID)const
{
	if( varID < 0 || varID >= getNbUsedVars() )
	{
		throwParsingExcep(MTDEFEXCEP_ItemNotFound, varID);	
	}

	return m_usedVariables[varID].symbol;
}

MTSTRING MTParserCompiler::getExpression()const
{
	return m_originalExpr;
}

void MTParserCompiler::throwParsingExcep(const MTCHAR *id, const MTCHAR *itemName)const throw(MTParserException)
{
	MTExcepData data	(id, 						
						MTEXCEPARG_ITEMNAME, itemName );
						
	
	MTTHROW(data)	

}

void MTParserCompiler::throwParsingExcep(const MTCHAR * id, int pos, const MTCHAR *itemName, int param)const throw(MTParserException)
{
	MTExcepData data	(id, 
						MTEXCEPARG_POS, MTTools::longToS(m_originalPos[pos]).c_str(), 
						MTEXCEPARG_ITEMNAME, itemName, 
						MTEXCEPARG_PARAM1, MTTools::longToS(param).c_str()	);
	
	MTTHROW(data)

}
void MTParserCompiler::throwParsingExcep(const MTCHAR * id, int pos)const throw(MTParserException)
{
	MTExcepData data	(id, 
						MTEXCEPARG_POS, MTTools::longToS(m_originalPos[pos]).c_str() );						
	
	MTTHROW(data)

}
void MTParserCompiler::throwParsingExcep(const MTCHAR * id, int pos, const MTCHAR *itemName)const throw(MTParserException)
{	

	MTExcepData data	(id, 
						MTEXCEPARG_POS, MTTools::longToS(m_originalPos[pos]).c_str(), 
						MTEXCEPARG_ITEMNAME, itemName );
						
	
	MTTHROW(data)

}

//**************************************
// MTVarStub 

MTParserCompiler::MTVarStub::MTVarStub ()
{
	m_pValue = NULL;
}

void MTParserCompiler::MTVarStub::setValuePtr(MTDOUBLE *pValue)
{
	m_pValue = pValue;
}

MTDOUBLE MTParserCompiler::MTVarStub::evaluate(unsigned int nbArgs, const MTDOUBLE *pArg)
{
	return *m_pValue;		
}
MTVariableI* MTParserCompiler::MTVarStub::spawn()
{
	return new MTVarStub;
}

//**************************************
// MTCompilerDefState

void MTCompilerDefState::onOp(const MTSTRING &opSymbol)
{
	if( !m_pParserCompiler->parseWord() )
	{
		return;
	}

	m_pParserCompiler->setArgValueFlag(true);
	m_pParserCompiler->setCurItemType(MTParserCompiler::e_ItemType_Operator);

	// this is a one argument operator if there is no left
	// argument:
	bool unaryOp = false;
	if( m_pParserCompiler->getPrevItemType() == MTParserCompiler::e_ItemType_None ||
		m_pParserCompiler->getPrevItemType() == MTParserCompiler::e_ItemType_Operator )
	{		
		unaryOp = true;
	}

	unsigned int opID;
	if( !m_pParserCompiler->getRegistrar()->findOp(opSymbol.c_str(), unaryOp, opID) )
	{					
		m_pParserCompiler->throwParsingExcep(MTPARSINGEXCEP_InvalidOpSyntax, m_pCompiler->getCurPos(), opSymbol.c_str());
	}				

	// advance the expression char cursor by the op symbol length
	m_pParserCompiler->setCurPos(m_pCompiler->getCurPos() + lstrlen(m_pParserCompiler->getRegistrar()->getOp(opID)->getSymbol())-1);

	// an expression cannot end with an expression...
	if( m_pCompiler->getCurPos() >= m_pParserCompiler->getExprLength()-1 )
	{					
		m_pParserCompiler->throwParsingExcep(MTPARSINGEXCEP_UnexpectedOp, m_pCompiler->getCurPos());
	}


	if( m_pParserCompiler->m_opStack.size() > 0 )
	{
		int prevOpPrecedence = m_pParserCompiler->m_opStack[m_pParserCompiler->m_opStack.size()-1].precedence;
		int curOpPrecedence;
		if( prevOpPrecedence != MTOPEN_BRACKET_PRECEDENCE )
		{						
			curOpPrecedence = m_pParserCompiler->getRegistrar()->getOp(opID)->getPrecedence();

			// Special case: if the two ops have the same precedence
			// and that the previous op is an unary operator
			// then that means that the curOp is the argument of the
			// previous op.  So, we must not pop the previous op.
			if( curOpPrecedence == prevOpPrecedence &&
				m_pParserCompiler->m_opStack[m_pParserCompiler->m_opStack.size()-1].itemType == MTParserCompiler::e_ItemType_Operator &&
				((MTOperatorI*)m_pParserCompiler->m_opStack[m_pParserCompiler->m_opStack.size()-1].pEval)->isUnaryOp() )
			{
				curOpPrecedence = prevOpPrecedence+1;	// skip the while
			}

			// destack op until op precedence > previous op.
			// the destasked op will be evaluated before.
			// if the precedence is equal, the rule is to evaluate
			// from left to right, so the previous op has greater precedence also
			while( curOpPrecedence <= prevOpPrecedence )
			{
				
				m_pParserCompiler->pushOnExprStack(m_pParserCompiler->m_opStack[m_pParserCompiler->m_opStack.size()-1]);								

				// remove the last op
				m_pParserCompiler->m_opStack.erase(m_pParserCompiler->m_opStack.begin()+m_pParserCompiler->m_opStack.size()-1);

				if( m_pParserCompiler->m_opStack.size() > 0 )
				{
					prevOpPrecedence = m_pParserCompiler->m_opStack[m_pParserCompiler->m_opStack.size()-1].precedence;
					if( prevOpPrecedence == MTOPEN_BRACKET_PRECEDENCE )
					{
						break; // open bracket halts the destacking process
					}
				}
				else
				{
					break;	// no more op in the stack
				}
			}
		}
	}
	
	// push the operator on the stack...				
	MTParserCompiler::SOpStackItem item;
	item.exprStrPos = m_pCompiler->getCurPos();
	if( unaryOp )
	{
		item.nbArgs = 1;
	}
	else
	{
		item.nbArgs = 2;
	}
	item.precedence = m_pParserCompiler->getRegistrar()->getOp(opID)->getPrecedence();	
	item.pEval = m_pParserCompiler->getRegistrar()->getOp(opID);	
	item.itemType = MTParserCompiler::e_ItemType_Operator;
	item.itemID = opID;
	m_pParserCompiler->m_opStack.push_back(item);

}
void MTCompilerDefState::onOpenBracket()
{
	if( !m_pParserCompiler->parseWord() )
	{
		return;
	}

	// if the next char is a closing bracket = nothing inside brackets!
	// example: 1+()
	// only function may use the () syntax when no argument needed
	if( m_pParserCompiler->getCurItemType() != MTParserCompiler::e_ItemType_Function &&
		m_pParserCompiler->m_nextChar == MTCLOSE_BRACKET )
	{
		m_pParserCompiler->throwParsingExcep(MTPARSINGEXCEP_InvalidSyntax, m_pCompiler->getCurPos());
	}
	
	// reset the value flag
	m_pParserCompiler->setArgValueFlag(false);
	
	
	MTParserCompiler::SOpStackItem item;
	item.exprStrPos = m_pCompiler->getCurPos();
	item.precedence = MTOPEN_BRACKET_PRECEDENCE;	
	item.itemType = MTParserCompiler::e_ItemType_None;
	m_pParserCompiler->m_opStack.push_back(item);
	
	m_pParserCompiler->pushCurItemType();	

}
void MTCompilerDefState::onCloseBracket()
{
	onCloseArgSeparator();

}

void MTCompilerDefState::onCloseArgSeparator()
{	
	if( !m_pParserCompiler->parseWord() )
	{
		return;
	}	

	// operator cannot be followed by a closing bracket or an argument separator
	if( m_pParserCompiler->getCurItemType() == MTParserCompiler::e_ItemType_Operator )
	{					
		m_pParserCompiler->throwParsingExcep(MTPARSINGEXCEP_UnexpectedOp, m_pCompiler->getCurPos());
	}	
	
	// pop the last itemType
	m_pParserCompiler->m_curItemTypeStack.erase(m_pParserCompiler->m_curItemTypeStack.begin()+m_pParserCompiler->m_curItemTypeStack.size()-1);								

	MTCHAR curChar = m_pParserCompiler->m_expression[m_pCompiler->getCurPos()];	

	if( curChar == m_pParserCompiler->m_syntax.argumentSeparator )
	{
		// argument separators are only allowed in functions
		if( m_pParserCompiler->getCurItemType() != MTParserCompiler::e_ItemType_Function )
		{
			m_pParserCompiler->throwParsingExcep(MTPARSINGEXCEP_InvalidArgSeparator, m_pCompiler->getCurPos());										
		}
		
		// the old itemType has been popped, so we
		// push a new clean itemType 
		m_pParserCompiler->pushCurItemType();

		// if the next char is a closing bracket = invalid syntax!
		// example: fct(x,y,)
		if( m_pParserCompiler->m_nextChar == MTCLOSE_BRACKET )
		{
			m_pParserCompiler->throwParsingExcep(MTPARSINGEXCEP_UselessArgSeparator, m_pCompiler->getCurPos());
		}
	}

	// destack until an open bracket is found
	int curOpPrecedence;
	int nbPoppedItems = 0;
	do
	{
		if( m_pParserCompiler->m_opStack.size() == 0 )
		{
			MTSTRING msg;
			if( curChar == MTCLOSE_BRACKET )
			{
				m_pParserCompiler->throwParsingExcep(MTPARSINGEXCEP_MissingOpenBracket, m_pCompiler->getCurPos());							
			}
			else
			{
				m_pParserCompiler->throwParsingExcep(MTPARSINGEXCEP_InvalidArgSeparator, m_pCompiler->getCurPos());							
			}					
		}

		curOpPrecedence = m_pParserCompiler->m_opStack[m_pParserCompiler->m_opStack.size()-1].precedence;

		if( curOpPrecedence != MTOPEN_BRACKET_PRECEDENCE )
		{					
			nbPoppedItems++;
			m_pParserCompiler->pushOnExprStack(m_pParserCompiler->m_opStack[m_pParserCompiler->m_opStack.size()-1]);						

			// remove the last op
			m_pParserCompiler->m_opStack.erase(m_pParserCompiler->m_opStack.begin()+m_pParserCompiler->m_opStack.size()-1);
		}
	}while(curOpPrecedence != MTOPEN_BRACKET_PRECEDENCE);				
	 
	bool isInc = m_pParserCompiler->incArgCountIfArgValue(m_pParserCompiler->m_opStack);
				
	if( nbPoppedItems == 0  )
	{
		// if no argument but there is an argument separator character...!
		// example: avg(,)
		if(curChar == m_pParserCompiler->m_syntax.argumentSeparator && !isInc)
		{
			m_pParserCompiler->throwParsingExcep(MTPARSINGEXCEP_UselessArgSeparator, m_pCompiler->getCurPos());
		}			
	}


	// only if we got a closing bracket, we erase the opening bracket.				
	if( curChar == MTCLOSE_BRACKET )
	{
		// erase the open bracket
		m_pParserCompiler->m_opStack.erase(m_pParserCompiler->m_opStack.begin()+m_pParserCompiler->m_opStack.size()-1);

		// syntax like -(x)+3, the (x) is a value, so
		// the current item should be a value and not an operator.
		// When the current item is a function like: sin(x)+3
		// then the current item remains a function.
		if( m_pParserCompiler->getCurItemType() != MTParserCompiler::e_ItemType_Function) 
		{
			m_pParserCompiler->setCurItemType(MTParserCompiler::e_ItemType_Value);
		}					
	}			
	
	bool valueFlag = false;
	if( curChar == MTCLOSE_BRACKET && (m_pParserCompiler->m_nextChar == MTCLOSE_BRACKET || m_pParserCompiler->m_nextChar == m_pParserCompiler->m_syntax.argumentSeparator))
	{
		// propagate the value to the next item
		// example: sin((3+2)).  The argument 3+2 argument must be propagated to the sin function 
		valueFlag = isInc;					
	}						
	
	m_pParserCompiler->setArgValueFlag(valueFlag);


}

void MTCompilerDefState::onArgSeparator()
{
	onCloseArgSeparator();
}




By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
Software Engineer working at a fun and smart startup company

Comments and Discussions