//+------------------------------------------------------------------+ //| GenericParser.mqh | //| Copyright 2026, Niquel Mendoza. | //| https://www.mql5.com/es/users/nique_372 | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Niquel Mendoza." #property link "https://www.mql5.com/es/users/nique_372" #property strict #ifndef EXPRESSEVALBYLEO_SRC_COMMON_GENERICPARSER_MQH #define EXPRESSEVALBYLEO_SRC_COMMON_GENERICPARSER_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "Def.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // La clase CGenericParser es una clase base universal para este repo // Que sirve para parsear Numeros\Strings\Calling functions etc. class CGenericParser { public: CGenericParser(void) {} ~CGenericParser(void) {} //--- static long FastAtoi(const string& text, const int start, const int end, const int sign); static long FastAtoiHex(const string& text, const int start, const int end); static void SubstrRange(const string& text, string& out, const int start, const int end); //--- static void FormatErr(const int err, string& err_msg); //--- template static bool ParseRawString(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token); template static bool UnescapeString(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token); template static bool ParseNumberNoHex(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token, const int start_corte); template static bool ParseNumber(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token); //--- template static void ParseVariable(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token); template static bool ParseFunction(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ static void CGenericParser::SubstrRange(const string &text, string &out, const int start, const int end) { const int len = (end - start) + 1; StringSetLength(out, len); for(int i = 0; i < len; i++) { out.SetChar(i, text[start + i]); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ static long CGenericParser::FastAtoi(const string& text, const int start, const int end, const int sign) { //--- Varialbes inciales long val = 0; int i = start; const int n_len = (end - start) + 1; //--- Salto de 4 en 4 const int end4 = start + n_len - 4; for(; i <= end4; i += 4) { val = val * 10000 + text[i] * 1000 + text[i + 1] * 100 + text[i + 2] * 10 + text[i + 3] - '0' * 1111; } //--- Llo que sobra for(; i <= end; i++) { val = val * 10 + (text[i] - '0'); } //--- Reotrnamos return val * sign; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ static long CGenericParser::FastAtoiHex(const string& text, const int start, const int end) { //Print(CharArrayToString(text, start, (end - start) + 1)); //--- long val = 0; int i = start + 2; // salta 0x //--- const int end2 = end - 1; for(; i <= end2; i += 2) { val = (val << 8) | (g_arr_mqlarticles_valid_hex_chars[uchar(text[i])] << 4) | g_arr_mqlarticles_valid_hex_chars[uchar(text[i + 1])]; } //--- for(; i <= end; i++) { val = (val << 4) | g_arr_mqlarticles_valid_hex_chars[text[i]]; } //Print(val); return val; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ static void CGenericParser::FormatErr(const int err, string &err_msg) { switch(err) { case GENERIC_ERR_PARSE_STRING_RAW_INICIO_NO_FOUND_EN_CUSTOM: err_msg = "String raw init not found, expected '(' "; break; case GENERIC_ERR_PARSE_STRING_RAW_SUPERO_EL_LEN: err_msg = "String raw init superated text len"; break; case GENERIC_ERR_PARSE_STRING_RAW_NO_CERRADO: err_msg = "String raw not closed, expected '(DELIMITER\"'"; break; case GENERIC_ERR_PARSE_DBL_TIENE_MAS_DE_DOS_PUNTOS: err_msg = "Double number has more two point"; break; case GENERIC_ERR_PARSE_DBL_TIENE_MAS_DE_DOS_MENOS: err_msg = "Double number has more two minos sing"; break; case GENERIC_ERR_PARSE_NUMERO_INVALIDO: err_msg = "Invalid number"; break; case GENERIC_ERR_PARSE_NUMERO_MAL_FORMADO: err_msg = "Number malformed"; break; case GENERIC_ERR_PARSE_NUMERO_HEX_INVALIDO: err_msg = "Invalid HEX Number"; break; case GENERIC_ERR_PARSE_STRING_NO_CERRADO: err_msg = "String not closed, expected char '\"' "; break; case GENERIC_ERR_PARSE_STRING_MALFORMADO_ACABA_CON_SLASH: err_msg = "String closed in char por especial chars.. '\\' "; break; case GENERIC_ERR_PARSE_SE_ESPERABA_UN_FIN_DE_FUNCION: err_msg = "Function not closed, expected char ')'"; break; case GENERIC_ERR_PARSE_SE_ESPERABA_UN_INICIO_DE_FUNCION: err_msg = "Function with not start (, expected char '('"; break; default: err_msg = "Unknow error"; break; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template static void CGenericParser::ParseVariable(const string &text, const int m_len, int &m_pos, int &m_last_err, int &m_err_pos, T& token) { //--- Ahora mismo en $ m_pos++; const int start = m_pos; //--- // Avanzamos hasta el siguiente espacio mientras que pos sea menor a len while(text[m_pos] != ' ' && m_pos < m_len) { m_pos++; } //--- SubstrRange(text, token.vs, start, m_pos - 1); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template static bool CGenericParser::ParseFunction(const string &text, const int m_len, int &m_pos, int& m_last_err, int& m_err_pos, T& token) { //--- Ahora mismo en @ o & int start = m_pos++; //--- while(text[m_pos] != '(') { if(m_pos >= m_len) { m_err_pos = start; m_last_err = GENERIC_ERR_PARSE_SE_ESPERABA_UN_INICIO_DE_FUNCION | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; return false; } m_pos++; } //--- SubstrRange(text, token.vs, start, m_pos - 1); //--- Parametros start = ++m_pos; // Luego del ( //--- while(text[m_pos] != ')') { if(m_pos >= m_len) { m_err_pos = start; // Inicio de la funcion m_last_err = GENERIC_ERR_PARSE_SE_ESPERABA_UN_FIN_DE_FUNCION | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; return false; } m_pos++; } //--- token.vs += OPS_TOKENIZER_CHAR_SEP + StringSubstrRange(text, start, m_pos - 1); // Parametros return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template static bool CGenericParser::ParseRawString(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token) { //--- m_pos += 2; // R"(10)" ahora nos uicmso ahi if(m_pos >= m_len) { m_last_err = GENERIC_ERR_PARSE_STRING_RAW_SUPERO_EL_LEN | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; m_err_pos = m_pos; return false; } //--- string find = ")\""; int l = 1; if(text[m_pos] != '(') { const int start = m_pos; while(text[m_pos] != '(') { if(m_pos >= m_len) { m_last_err = GENERIC_ERR_PARSE_STRING_RAW_INICIO_NO_FOUND_EN_CUSTOM | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; m_err_pos = start; return false; } m_pos++; } find = ")" + StringSubstrRange(text, start, m_pos - 1) + "\""; l = StringLen(find) - 1; } //--- m_pos++; const int start = m_pos; //--- //token.type = ; ya se puso el tipo de raw m_pos = text.Find(find, m_pos); if(m_pos == -1) { m_last_err = GENERIC_ERR_PARSE_STRING_RAW_NO_CERRADO | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; //Print(GENERIC_ERR_PARSE_STRING_RAW_NO_CERRADO, " | " , AST_LOGIC_ERR_TYPE_EXTERNAL , " << " , AST_LOGIC_ERR_TYPE_START_BIT , " = " , m_last_err); m_err_pos = start; return false; } //--- token.vs = StringSubstrRange(text, start, m_pos - 1); m_pos += l; //--- return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // empieza con - template static bool CGenericParser::ParseNumberNoHex(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token, const int start_corte) { //--- #define OPSTOKENIZER_NUMBER_TYPE_INT 1 #define OPSTOKENIZER_NUMBER_TYPE_DBL 2 //--- uint8_t f = OPSTOKENIZER_NUMBER_TYPE_INT; const int start = m_pos; //--- //Print(start); while(m_pos < m_len) { if(text[m_pos] == '-') // - puede ser e { if(f != OPSTOKENIZER_NUMBER_TYPE_DBL) f = OPSTOKENIZER_NUMBER_TYPE_DBL; else if(f == OPSTOKENIZER_NUMBER_TYPE_DBL) { m_last_err = GENERIC_ERR_PARSE_DBL_TIENE_MAS_DE_DOS_MENOS | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; m_err_pos = m_pos; return false; } } else if(text[m_pos] == '.') { if(f != OPSTOKENIZER_NUMBER_TYPE_DBL) f = OPSTOKENIZER_NUMBER_TYPE_DBL; else if(f == OPSTOKENIZER_NUMBER_TYPE_DBL) { m_last_err = GENERIC_ERR_PARSE_DBL_TIENE_MAS_DE_DOS_PUNTOS | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; m_err_pos = m_pos; return false; } } else if(IsNotDigit(text[m_pos])) { break; // Ya no es un numero salimos } m_pos++; } //Print(m_pos); //--- if(start == m_pos) { m_last_err = GENERIC_ERR_PARSE_NUMERO_INVALIDO | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; m_err_pos = start; return false; } //--- if(f == OPSTOKENIZER_NUMBER_TYPE_DBL) { token.type |= int(AST_LOGIC_NUMBER_DOUBLE) << AST_LOGIC_EXTRA_TYPE_START_BIT; //--- SubstrRange(text, token.vs, (start_corte == -1 ? start : start_corte), --m_pos); token.v.double_value = double(token.vs); } else { token.type |= int(AST_LOGIC_NUMBER_INTEGER) << AST_LOGIC_EXTRA_TYPE_START_BIT; token.v.long_value = FastAtoi(text, (start_corte == -1 ? start : start_corte), --m_pos, -1); } return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template static bool CGenericParser::ParseNumber(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token) { //--- #define OPSTOKENIZER_NUMBER_TYPE_HEX 3 uint8_t f = OPSTOKENIZER_NUMBER_TYPE_INT; const int start = m_pos; //--- if(text[m_pos] == '0' && m_pos + 1 < m_len && (text[m_pos + 1] == 'x' || text[m_pos + 1] == 'X')) { f = OPSTOKENIZER_NUMBER_TYPE_HEX; m_pos += 2; } //--- while(m_pos < m_len) { if(f == OPSTOKENIZER_NUMBER_TYPE_HEX) { if(g_arr_mqlarticles_valid_hex_chars[uchar(text[m_pos])] == 0xFF) { break; } } else if(text[m_pos] == '-') { if(f != OPSTOKENIZER_NUMBER_TYPE_DBL) f = OPSTOKENIZER_NUMBER_TYPE_DBL; else if(f == OPSTOKENIZER_NUMBER_TYPE_DBL) { m_last_err = GENERIC_ERR_PARSE_DBL_TIENE_MAS_DE_DOS_MENOS | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; m_err_pos = m_pos; return false; } } else if(text[m_pos] == '.') { if(f != OPSTOKENIZER_NUMBER_TYPE_DBL) f = OPSTOKENIZER_NUMBER_TYPE_DBL; else if(f == OPSTOKENIZER_NUMBER_TYPE_DBL) { m_last_err = GENERIC_ERR_PARSE_DBL_TIENE_MAS_DE_DOS_PUNTOS | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; m_err_pos = m_pos; return false; } } else if(IsNotDigit(text[m_pos])) { break; // Salimos ya no es un numero } //--- m_pos++; } //--- if(start == m_pos) { m_last_err = GENERIC_ERR_PARSE_NUMERO_INVALIDO | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; m_err_pos = start; return false; } //--- if(f == OPSTOKENIZER_NUMBER_TYPE_DBL) { token.type |= int(AST_LOGIC_NUMBER_DOUBLE) << AST_LOGIC_EXTRA_TYPE_START_BIT; SubstrRange(text, token.vs, start, --m_pos); token.v.double_value = double(token.vs); } else { token.type |= int(AST_LOGIC_NUMBER_INTEGER) << AST_LOGIC_EXTRA_TYPE_START_BIT; token.v.long_value = f == OPSTOKENIZER_NUMBER_TYPE_HEX ? FastAtoiHex(text, start, --m_pos) : FastAtoi(text, start, --m_pos, 1); } return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template bool CGenericParser::UnescapeString(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token) { //--- Ahora mismo en " m_pos++; //--- Luego de " const int start_pos = m_pos - 1; int reserve = 32; StringSetLength(token.vs, reserve); // por defecto int pos = 0; //--- while(m_pos < m_len && text[m_pos] != '\n') { if(text[m_pos] == '\\') { if(++m_pos < m_len) { const ushort next = text[m_pos]; switch(next) { case '"': token.vs.SetChar(pos++, '"'); break; case '\\': token.vs.SetChar(pos++, '\\'); break; case '/': token.vs.SetChar(pos++, '/'); break; case 'b': token.vs.SetChar(pos++, '\x08'); break; case 'f': token.vs.SetChar(pos++, '\f'); break; case 'n': token.vs.SetChar(pos++, '\n'); break; case 'r': token.vs.SetChar(pos++, '\r'); break; case 't': token.vs.SetChar(pos++, '\t'); break; default: token.vs.SetChar(pos++, next); break; } //--- if(pos >= reserve) { reserve <<= 1; StringSetLength(token.vs, reserve); } } else { m_last_err = GENERIC_ERR_PARSE_STRING_MALFORMADO_ACABA_CON_SLASH | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; m_err_pos = m_pos - 1 ; return false; } //--- m_pos++; continue; } //--- if(text[m_pos] == '"') { StringSetLength(token.vs, pos); return true; } //--- token.vs.SetChar(pos++, text[m_pos]); if(pos >= reserve) { reserve <<= 1; StringSetLength(token.vs, reserve); } //--- m_pos++; } //--- m_last_err = GENERIC_ERR_PARSE_STRING_NO_CERRADO | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT; m_err_pos = start_pos; // Guardamos el incio del string return false; } //+------------------------------------------------------------------+ #endif // EXPRESSEVALBYLEO_SRC_COMMON_GENERICPARSER_MQH