ExpressEvalByLeo/Src/Common/GenericParser.mqh

580 lines
17 KiB
MQL5
Raw Permalink Normal View History

2026-05-11 10:55:56 -05:00
//+------------------------------------------------------------------+
//| 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);
2026-05-11 11:46:04 -05:00
//---
static void FormatErr(const int err, string& err_msg);
2026-05-11 10:55:56 -05:00
//---
template <typename T>
static bool ParseRawString(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token);
template <typename T>
static bool UnescapeString(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token);
template <typename T>
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 <typename T>
static bool ParseNumber(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token);
//---
template <typename T>
static void ParseVariable(const string& text, const int m_len, int& m_pos, int& m_last_err, int& m_err_pos, T& token);
template <typename T>
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++)
{
2026-05-14 14:13:20 -05:00
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;
}
2026-05-11 11:46:04 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
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;
}
}
2026-05-11 10:55:56 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
template <typename T>
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);
2026-05-11 10:55:56 -05:00
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
template <typename T>
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 &
2026-05-14 16:54:23 -05:00
int start = m_pos++;
2026-05-11 10:55:56 -05:00
//---
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);
2026-05-11 10:55:56 -05:00
//--- 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
2026-05-11 10:55:56 -05:00
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
template <typename T>
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;
2026-05-11 11:46:04 -05:00
//Print(GENERIC_ERR_PARSE_STRING_RAW_NO_CERRADO, " | " , AST_LOGIC_ERR_TYPE_EXTERNAL , " << " , AST_LOGIC_ERR_TYPE_START_BIT , " = " , m_last_err);
2026-05-11 10:55:56 -05:00
m_err_pos = start;
return false;
}
//---
token.vs = StringSubstrRange(text, start, m_pos - 1);
2026-05-11 10:55:56 -05:00
m_pos += l;
//---
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// empieza con -
2026-05-11 10:55:56 -05:00
template <typename T>
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);
2026-05-13 12:07:09 -05:00
while(m_pos < m_len)
2026-05-11 10:55:56 -05:00
{
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]))
{
2026-05-13 12:07:09 -05:00
break; // Ya no es un numero salimos
2026-05-11 10:55:56 -05:00
}
m_pos++;
}
//Print(m_pos);
//---
if(start == m_pos)
{
2026-05-11 11:46:04 -05:00
m_last_err = GENERIC_ERR_PARSE_NUMERO_INVALIDO | int(AST_LOGIC_ERR_TYPE_EXTERNAL) << AST_LOGIC_ERR_TYPE_START_BIT;
2026-05-11 10:55:56 -05:00
m_err_pos = start;
return false;
}
2026-05-11 10:55:56 -05:00
//---
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);
}
2026-05-11 10:55:56 -05:00
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
template <typename T>
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;
}
//---
2026-05-13 12:07:09 -05:00
while(m_pos < m_len)
2026-05-11 10:55:56 -05:00
{
if(f == OPSTOKENIZER_NUMBER_TYPE_HEX)
{
if(g_arr_mqlarticles_valid_hex_chars[uchar(text[m_pos])] == 0xFF)
{
2026-05-13 12:07:09 -05:00
break;
2026-05-11 10:55:56 -05:00
}
}
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]))
{
2026-05-13 12:07:09 -05:00
break; // Salimos ya no es un numero
2026-05-11 10:55:56 -05:00
}
//---
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);
}
2026-05-11 10:55:56 -05:00
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
template <typename T>
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;
2026-05-13 16:29:52 -05:00
StringSetLength(token.vs, reserve); // por defecto
2026-05-11 10:55:56 -05:00
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++, '"');
2026-05-11 10:55:56 -05:00
break;
case '\\':
token.vs.SetChar(pos++, '\\');
2026-05-11 10:55:56 -05:00
break;
case '/':
token.vs.SetChar(pos++, '/');
2026-05-11 10:55:56 -05:00
break;
case 'b':
token.vs.SetChar(pos++, '\x08');
2026-05-11 10:55:56 -05:00
break;
case 'f':
token.vs.SetChar(pos++, '\f');
2026-05-11 10:55:56 -05:00
break;
case 'n':
token.vs.SetChar(pos++, '\n');
2026-05-11 10:55:56 -05:00
break;
case 'r':
token.vs.SetChar(pos++, '\r');
2026-05-11 10:55:56 -05:00
break;
case 't':
token.vs.SetChar(pos++, '\t');
2026-05-11 10:55:56 -05:00
break;
default:
token.vs.SetChar(pos++, next);
2026-05-11 10:55:56 -05:00
break;
}
//---
if(pos >= reserve)
{
reserve <<= 1;
StringSetLength(token.vs, reserve);
2026-05-11 10:55:56 -05:00
}
}
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);
2026-05-11 10:55:56 -05:00
return true;
}
//---
token.vs.SetChar(pos++, text[m_pos]);
2026-05-11 10:55:56 -05:00
if(pos >= reserve)
{
reserve <<= 1;
StringSetLength(token.vs, reserve);
2026-05-11 10:55:56 -05:00
}
//---
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