#include "Dictionary.mqh" class CReversePolishNotation { protected: enum RevPol_TokenType { RP_TOKEN_NUMBER, RP_TOKEN_VARIABLE, RP_TOKEN_FUNCTION, RP_TOKEN_OPERATOR, RP_TOKEN_PARENTESIS }; enum RevPol_FunctionType { RP_FUNC_MIN, RP_FUNC_MAX, RP_FUNC_SQRT, RP_FUNC_ABS, RP_FUNC_LOGN, RP_FUNC_LOG10, RP_FUNC_ROUND, RP_FUNC_UNARY_SUB }; enum RevPol_OperatorType { RP_OPER_ADD, RP_OPER_SUB, RP_OPER_MUL, RP_OPER_DIV, RP_OPER_EXP }; enum RevPol_ParentesisType { RP_PAR_LEFT, RP_PAR_RIGHT }; struct RPNToken { RevPol_TokenType type; int second_type; int precedence; bool left_asociative; int function_arguments; double number; string variable; }; static void ConvertStringToTokens(const string input_string, RPNToken &token_array[]); static double Evaluate_RPN(RPNToken &to_execute[], CDictionary* dict=NULL); static void Infix_to_ReversePolish(RPNToken &input_array[], RPNToken &output_array[]); static void PreProcessString(string &inp_str); private: static string GetTokenArrayString(const RPNToken &array[]); static bool IsNumber(const string s); static bool IsVariable(const string s); static double GetCustomVariable(string variable, CDictionary* dict=NULL); static string TokenToString(const RPNToken &token); public: static double EvaluateInfixString(string to_evaluate, CDictionary* variables=NULL); static double EvaluatePostfixString(string to_evaluate, CDictionary* variables=NULL); static CDictionary* GetDefaultDictionary(); }; #define CASE_OPERATOR(opStr, tkn_type, sub_type, prec, l, is_n) \ else if (tokens[i]==opStr) \ { \ token_array[n-1].type = tkn_type; \ token_array[n-1].precedence = prec; \ token_array[n-1].second_type = sub_type; \ token_array[n-1].left_asociative = l; \ last_is_number = is_n; \ } #define CASE_FUNCTION(opStr, sub_type, args) \ else if (tokens[i]==opStr) \ { \ token_array[n-1].type = RP_TOKEN_FUNCTION; \ token_array[n-1].function_arguments = args; \ token_array[n-1].second_type = sub_type; \ last_is_number = false; \ } void CReversePolishNotation::ConvertStringToTokens(const string input_string, RPNToken &token_array[]) { string tokens[]; StringSplit(input_string, ' ', tokens); int total = ArraySize(tokens); ArrayResize(token_array, 0, total); int n=0; bool last_is_number = false; for (int i=0; i0 //Not empty // Not left parentesis && (stack[s-1].type!=RP_TOKEN_PARENTESIS || stack[s-1].second_type!=RP_PAR_LEFT) // && (stack[s-1].type==RP_TOKEN_FUNCTION || stack[s-1].precedence > input_array[i].precedence || (stack[s-1].precedence == input_array[i].precedence && input_array[i].left_asociative) ) ) { //Print("Pop stack to output: ", TokenToString(stack[s-1])); POP_TO_OUTPUT } //Print("Push token to stack: ", TokenToString(input_array[i])); PUSH_FROM_INPUT break; } case RP_TOKEN_PARENTESIS: { if (input_array[i].second_type==RP_PAR_LEFT) { //Print("Push token to stack: ", TokenToString(input_array[i])); PUSH_FROM_INPUT } else { while (s>0) { if (stack[s-1].type==RP_TOKEN_PARENTESIS && stack[s-1].second_type==RP_PAR_LEFT) break; else { //Print("Pop stack to output: ", TokenToString(stack[s-1])); POP_TO_OUTPUT } } if (s==0) // Stack empty without left parenthesis { MISMATCH_ERROR } //Print("Pop stack: ", TokenToString(stack[s-1])); s--; ArrayResize(stack, s); if (s>0 && stack[s-1].type==RP_TOKEN_FUNCTION) { //Print("Pop stack to output: ", TokenToString(stack[s-1])); POP_TO_OUTPUT } } break; } } i++; } while (s>0) { if (stack[s-1].type==RP_TOKEN_PARENTESIS && stack[s-1].second_type==RP_PAR_LEFT) { MISMATCH_ERROR } //Print("Pop stack to output: ", TokenToString(stack[s-1])); POP_TO_OUTPUT } ArrayResize(output_array, o); } string CReversePolishNotation::GetTokenArrayString(const RPNToken &array[]) { int size = ArraySize(array); if (size==0) return ""; string output = TokenToString(array[0]); for (int i=1; i= 0; iPos--) { int c = StringGetCharacter(s, iPos); if( (c < '0' || c > '9') && c != '.') return false; } return true; } bool CReversePolishNotation::IsVariable(const string s) { if (StringLen(s)<=0) return false; return s[0]=='#'; } #define POP_OPERAND(var) \ var = stack[s-1].number; \ s--; \ ArrayResize(stack, s); double CReversePolishNotation::Evaluate_RPN(RPNToken &to_execute[], CDictionary* dict=NULL) { int total = ArraySize(to_execute); RPNToken stack[]; int s=0; double o2; for (int i=0; i0.0)) { #ifndef NO_DEBUG_PRINT Print("ERROR: Division by 0"); #endif return 0.0; } stack[s-1].number /= o2; break; case RP_OPER_EXP: stack[s-1].number = MathPow(stack[s-1].number, o2); break; } //Result is in stack break; } case RP_TOKEN_FUNCTION: { if (s1) { #ifndef NO_DEBUG_PRINT Print(GetTokenArrayString(to_execute)); Print("ERROR: Unexpected number (operator may be missing or function has excess arguments)"); #endif } if (s>0) return stack[s-1].number; return 0.0; } string CReversePolishNotation::TokenToString(const RPNToken &token) { switch (token.type) { case RP_TOKEN_NUMBER: return DoubleToString(token.number, (MathMod(token.number, 1.0)>0.0)?8:0); case RP_TOKEN_VARIABLE: return "~"+token.variable+"~"; case RP_TOKEN_OPERATOR: { switch (token.second_type) { case RP_OPER_ADD: return "+"; case RP_OPER_SUB: return "-"; case RP_OPER_MUL: return "*"; case RP_OPER_DIV: return "/"; case RP_OPER_EXP: return "^"; } return ""; } case RP_TOKEN_FUNCTION: { switch (token.second_type) { case RP_FUNC_MAX: return "max"; case RP_FUNC_MIN: return "min"; case RP_FUNC_SQRT: return "sqrt"; case RP_FUNC_ABS: return "abs"; case RP_FUNC_LOGN: return "logn"; case RP_FUNC_LOG10: return "log10"; case RP_FUNC_ROUND: return "round"; case RP_FUNC_UNARY_SUB: return "u-"; } return ""; } case RP_TOKEN_PARENTESIS: { if (token.second_type==RP_PAR_LEFT) return "("; else return ")"; } } return ""; } double CReversePolishNotation::EvaluateInfixString(string to_evaluate, CDictionary* variables=NULL) { PreProcessString(to_evaluate); RPNToken array[], output[]; ConvertStringToTokens(to_evaluate, array); Infix_to_ReversePolish(array, output); return Evaluate_RPN(output, variables); } double CReversePolishNotation::EvaluatePostfixString(string to_evaluate, CDictionary* variables=NULL) { PreProcessString(to_evaluate); RPNToken array[]; ConvertStringToTokens(to_evaluate, array); return Evaluate_RPN(array, variables); } void CReversePolishNotation::PreProcessString(string &inp_str) { StringReplace(inp_str, "+", " + "); StringReplace(inp_str, "-", " - "); StringReplace(inp_str, "*", " * "); StringReplace(inp_str, "/", " / "); StringReplace(inp_str, "^", " ^ "); StringReplace(inp_str, "(", " ( "); StringReplace(inp_str, ")", " ) "); StringReplace(inp_str, ",", " "); StringReplace(inp_str, "#", " #"); } double CReversePolishNotation::GetCustomVariable(string variable, CDictionary* dict=NULL) { if (CheckPointer(dict)==POINTER_INVALID) { #ifndef NO_DEBUG_PRINT Print("Invalid Variable Dictionary"); #endif return 0.0; } double result; if (dict.Get(variable, result)) return result; #ifndef NO_DEBUG_PRINT Print("Unknown variable: ", variable); #endif return 0.0; } CDictionary* CReversePolishNotation::GetDefaultDictionary(void) { CDictionary* dict = new CDictionary(); dict.Set("#PI", 3.14159265359); return dict; }