/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#ifndef EXPRESSION_20070402_TCC_
#define EXPRESSION_20070402_TCC_

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
template <class T>
Expression<T>::Expression(const QString &s, variable_getter_t vg, void *vg_param, memory_reader_t mr, void *mr_param) : 
		m_Expression(s), m_ExpressionPtr(m_Expression.begin()), 
		m_VariableGetter(vg), m_VariableGetterParam(vg_param), 
		m_MemoryReader(mr), m_MemoryReaderParam(mr_param) {
}

//------------------------------------------------------------------------------
// Name: eval_exp(T &result)
// Desc: private entry point with sanity check
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp(T &result) {
	if(m_Token.m_Type == Token::UNKNOWN) {
		throw ExpressionError(ExpressionError::SYNTAX);
	}
	
	eval_exp0(result);
}

//------------------------------------------------------------------------------
// Name: eval_exp0(T &result)
// Desc: logic
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp0(T &result) {
	eval_exp1(result);

	for(Token op = m_Token; op.m_Operator == Token::LOGICAL_AND || op.m_Operator == Token::LOGICAL_OR; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp1(partial_value);

		// add or subtract
		switch(op.m_Operator) {
		case Token::LOGICAL_AND:
			result = result && partial_value;
			break;
		case Token::LOGICAL_OR:
			result = result || partial_value;
			break;
		default:
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: eval_exp1(T &result)
// Desc: binary logic
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp1(T &result) {
	eval_exp2(result);

	for(Token op = m_Token; op.m_Operator == Token::AND || op.m_Operator == Token::OR || op.m_Operator == Token::XOR; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp2(partial_value);

		// add or subtract
		switch(op.m_Operator) {
		case Token::AND:
			result &= partial_value;
			break;
		case Token::OR:
			result |= partial_value;
			break;
		case Token::XOR:
			result ^= partial_value;
			break;
		default:
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: eval_exp2(T &result)
// Desc: comparisons
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp2(T &result) {
	eval_exp3(result);

	for(Token op = m_Token; op.m_Operator == Token::LT || op.m_Operator == Token::LE || op.m_Operator == Token::GT || op.m_Operator == Token::GE || op.m_Operator == Token::EQ || op.m_Operator == Token::NE; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp3(partial_value);

		// perform the relational operation
		switch(op.m_Operator) {
		case Token::LT:
			result = result < partial_value;
			break;
		case Token::LE:
			result = result <= partial_value;
			break;
		case Token::GT:
			result = result > partial_value;
			break;
		case Token::GE:
			result = result >= partial_value;
			break;
		case Token::EQ:
			result = result == partial_value;
			break;
		case Token::NE:
			result = result != partial_value;
			break;
		default:
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: eval_exp3(T &result)
// Desc: shifts
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp3(T &result) {
	eval_exp4(result);

	for(Token op = m_Token; op.m_Operator == Token::RSHFT || op.m_Operator == Token::LSHFT; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp4(partial_value);

		// perform the shift operation
		switch(op.m_Operator) {
		case Token::LSHFT:
			result <<= partial_value;
			break;
		case Token::RSHFT:
			result >>= partial_value;
			break;
		default:
			break;
		}
	}	
}

//------------------------------------------------------------------------------
// Name: eval_exp4(T &result)
// Desc: addition/subtraction
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp4(T &result) {
	eval_exp5(result);

	for(Token op = m_Token; op.m_Operator == Token::PLUS || op.m_Operator == Token::MINUS; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp5(partial_value);

		// add or subtract
		switch(op.m_Operator) {
		case Token::PLUS:
			result += partial_value;
			break;
		case Token::MINUS:
			result -= partial_value;
			break;
		default:
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: eval_exp5(T &result)
// Desc: multiplication/division
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp5(T &result) {
	eval_exp6(result);

	for(Token op = m_Token; op.m_Operator == Token::MUL || op.m_Operator == Token::DIV || op.m_Operator == Token::MOD; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp6(partial_value);

		// mul, div, or modulus
		switch(op.m_Operator) {
		case Token::MUL:
			result *= partial_value;
			break;
		case Token::DIV:
			if(partial_value == 0) {
				throw ExpressionError(ExpressionError::DIVIDE_BY_ZERO);
			}
			result /= partial_value;
			break;
		case Token::MOD:
			if(partial_value == 0) {
				throw ExpressionError(ExpressionError::DIVIDE_BY_ZERO);
			}
			result %= partial_value;
			break;
		default:
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: eval_exp6(T &result)
// Desc: unary expressions
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp6(T &result) {

	Token op = m_Token;
	if(op.m_Operator == Token::PLUS || op.m_Operator == Token::MINUS || op.m_Operator == Token::CMP || op.m_Operator == Token::NOT) {
		getToken();
	}

	eval_exp7(result);

	switch(op.m_Operator) {
	case Token::PLUS:
		// this may seems like a waste, but unary + can be overloaded for a type
		// to have a non-nop effect!
		result = +result;
		break;
	case Token::MINUS:
		result = -result;
		break;
	case Token::CMP:
		result = ~result;
		break;
	case Token::NOT:
		result = !result;
		break;
	default:
		break;
	}
}

//------------------------------------------------------------------------------
// Name: eval_exp7(T &result)
// Desc: sub-expressions
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp7(T &result) {

	switch(m_Token.m_Operator) {
	case Token::LPAREN:
		getToken();

		// get sub-expression
		eval_exp0(result);

		if(m_Token.m_Operator != Token::RPAREN) {
			throw ExpressionError(ExpressionError::UNBALANCED_PARENS);
		}

		getToken();
		break;
	case Token::RPAREN:
		throw ExpressionError(ExpressionError::INCORRECT_PARENS);
		break;
	case Token::LBRACE:
		do {
			getToken();

			// get sub-expression
			T effective_address;
			eval_exp0(effective_address);

			if(m_MemoryReader != 0) {
				bool ok;
				ExpressionError error;
								
				result = (*m_MemoryReader)(effective_address, ok, error, m_MemoryReaderParam);
				if(!ok) {
					throw error;
				}
			} else {
				throw ExpressionError(ExpressionError::CANNOT_READ_MEMORY);
			}

			if(m_Token.m_Operator != Token::RBRACE) {
				throw ExpressionError(ExpressionError::UNBALANCED_BRACES);
			}

			getToken();
		} while(0);
		break;
	case Token::RBRACE:
		throw ExpressionError(ExpressionError::INCORRECT_BRACES);
		break;
	default:
		eval_atom(result);
		break;
	
	}
}

//------------------------------------------------------------------------------
// Name: eval_atom(T &result)
// Desc: atoms (variables/constants)
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_atom(T &result) {

	switch(m_Token.m_Type) {	
	case Token::VARIABLE:
		if(m_VariableGetter != 0) {
			bool ok;
			ExpressionError error;
			result = m_VariableGetter(m_Token.m_Data, ok, error, m_VariableGetterParam);
			if(!ok) {
				throw error;
			}			
		} else {
			throw ExpressionError(ExpressionError::UNKNOWN_VARIABLE);
		}
		getToken();	
		break;
	case Token::NUMBER:
		bool ok;
		result = m_Token.m_Data.toULongLong(&ok, 0);
		if(!ok) {
			throw ExpressionError(ExpressionError::INVALID_NUMBER);
		}
		getToken();	
		break;
	default:
		throw ExpressionError(ExpressionError::SYNTAX);
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::getToken() {
	
	// clear previous token
	m_Token = Token();

	// eat up white space
	while(m_ExpressionPtr != m_Expression.end() && m_ExpressionPtr->isSpace()) {
		++m_ExpressionPtr;
	}

	if(m_ExpressionPtr != m_Expression.end()) {

		// get the token
		switch(m_ExpressionPtr->toAscii()) {
		case '(':
			++m_ExpressionPtr;
			m_Token.set("(", Token::LPAREN, Token::OPERATOR);
			break;
		case ')':
			++m_ExpressionPtr;
			m_Token.set(")", Token::RPAREN, Token::OPERATOR);
			break;
		case '[':
			++m_ExpressionPtr;
			m_Token.set("[", Token::LBRACE, Token::OPERATOR);
			break;
		case ']':
			++m_ExpressionPtr;
			m_Token.set("]", Token::RBRACE, Token::OPERATOR);
			break;
		case '!':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '=') {
				++m_ExpressionPtr;
				m_Token.set("!=", Token::NE, Token::OPERATOR);
			} else {
				m_Token.set("!", Token::NOT, Token::OPERATOR);
			}
			break;
		case '+':
			++m_ExpressionPtr;
			m_Token.set("+", Token::PLUS, Token::OPERATOR);
			break;
		case '-':
			++m_ExpressionPtr;
			m_Token.set("-", Token::MINUS, Token::OPERATOR);
			break;
		case '*':
			++m_ExpressionPtr;
			m_Token.set("*", Token::MUL, Token::OPERATOR);
			break;
		case '/':
			++m_ExpressionPtr;
			m_Token.set("/", Token::DIV, Token::OPERATOR);
			break;
		case '%':
			++m_ExpressionPtr;
			m_Token.set("%", Token::MOD, Token::OPERATOR);
			break;
		case '&':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '&') {
				++m_ExpressionPtr;
				m_Token.set("&&", Token::LOGICAL_AND, Token::OPERATOR);
			} else {
				m_Token.set("&", Token::AND, Token::OPERATOR);
			}
			break;
		case '|':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '|') {
				++m_ExpressionPtr;
				m_Token.set("||", Token::LOGICAL_OR, Token::OPERATOR);
			} else {
				m_Token.set("|", Token::OR, Token::OPERATOR);
			}
			break;
		case '^':
			++m_ExpressionPtr;
			m_Token.set("^", Token::XOR, Token::OPERATOR);
			break;
		case '~':
			++m_ExpressionPtr;
			m_Token.set("~", Token::CMP, Token::OPERATOR);
			break;
		case '=':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '=') {
				++m_ExpressionPtr;
				m_Token.set("==", Token::EQ, Token::OPERATOR);
			} else {
				throw ExpressionError(ExpressionError::SYNTAX);
			}
			break;
		case '<':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '<') {
				++m_ExpressionPtr;
				m_Token.set("<<", Token::LSHFT, Token::OPERATOR);
			} else if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '=') {
				++m_ExpressionPtr;
				m_Token.set("<=", Token::LE, Token::OPERATOR);
			} else {
				m_Token.set("<", Token::LT, Token::OPERATOR);
			}
			break;
		case '>':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '>') {
				++m_ExpressionPtr;
				m_Token.set(">>", Token::RSHFT, Token::OPERATOR);
			} else if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '=') {
				++m_ExpressionPtr;
				m_Token.set(">=", Token::GE, Token::OPERATOR);
			} else {
				m_Token.set(">", Token::GT, Token::OPERATOR);
			}
			break;
		default:
			// is it a numerical constant?
			if(m_ExpressionPtr->isDigit()) {
				QString tempString;

				while(m_ExpressionPtr != m_Expression.end() && !isDelim(*m_ExpressionPtr)) {
					tempString += *m_ExpressionPtr++;
				}

				m_Token.set(tempString, Token::NONE, Token::NUMBER);
			} else {
				// it must be a variable, get its name
				QString tempString;

				while(m_ExpressionPtr != m_Expression.end() && !isDelim(*m_ExpressionPtr)) {
					tempString += *m_ExpressionPtr++;
				}

				m_Token.set(tempString, Token::NONE, Token::VARIABLE);
			}
			break;
		}
	}		
}

#endif
