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) {}
|
|
|
|
|
|
2026-05-13 16:27:54 -05:00
|
|
|
//---
|
|
|
|
|
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);
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-13 16:27:54 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
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]);
|
2026-05-13 16:27:54 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
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++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
2026-05-13 16:27:54 -05:00
|
|
|
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++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
2026-05-13 16:27:54 -05:00
|
|
|
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++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
2026-05-13 16:27:54 -05:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
2026-05-13 16:27:54 -05:00
|
|
|
token.vs = StringSubstrRange(text, start, m_pos - 1);
|
2026-05-11 10:55:56 -05:00
|
|
|
m_pos += l;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-05-13 16:27:54 -05:00
|
|
|
// 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-13 16:27:54 -05:00
|
|
|
|
2026-05-11 10:55:56 -05:00
|
|
|
//---
|
2026-05-13 16:27:54 -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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
2026-05-13 16:27:54 -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, --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 '"':
|
2026-05-13 16:27:54 -05:00
|
|
|
token.vs.SetChar(pos++, '"');
|
2026-05-11 10:55:56 -05:00
|
|
|
break;
|
|
|
|
|
case '\\':
|
2026-05-13 16:27:54 -05:00
|
|
|
token.vs.SetChar(pos++, '\\');
|
2026-05-11 10:55:56 -05:00
|
|
|
break;
|
|
|
|
|
case '/':
|
2026-05-13 16:27:54 -05:00
|
|
|
token.vs.SetChar(pos++, '/');
|
2026-05-11 10:55:56 -05:00
|
|
|
break;
|
|
|
|
|
case 'b':
|
2026-05-13 16:27:54 -05:00
|
|
|
token.vs.SetChar(pos++, '\x08');
|
2026-05-11 10:55:56 -05:00
|
|
|
break;
|
|
|
|
|
case 'f':
|
2026-05-13 16:27:54 -05:00
|
|
|
token.vs.SetChar(pos++, '\f');
|
2026-05-11 10:55:56 -05:00
|
|
|
break;
|
|
|
|
|
case 'n':
|
2026-05-13 16:27:54 -05:00
|
|
|
token.vs.SetChar(pos++, '\n');
|
2026-05-11 10:55:56 -05:00
|
|
|
break;
|
|
|
|
|
case 'r':
|
2026-05-13 16:27:54 -05:00
|
|
|
token.vs.SetChar(pos++, '\r');
|
2026-05-11 10:55:56 -05:00
|
|
|
break;
|
|
|
|
|
case 't':
|
2026-05-13 16:27:54 -05:00
|
|
|
token.vs.SetChar(pos++, '\t');
|
2026-05-11 10:55:56 -05:00
|
|
|
break;
|
|
|
|
|
default:
|
2026-05-13 16:27:54 -05:00
|
|
|
token.vs.SetChar(pos++, next);
|
2026-05-11 10:55:56 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(pos >= reserve)
|
|
|
|
|
{
|
|
|
|
|
reserve <<= 1;
|
2026-05-13 16:27:54 -05:00
|
|
|
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] == '"')
|
|
|
|
|
{
|
2026-05-13 16:27:54 -05:00
|
|
|
StringSetLength(token.vs, pos);
|
2026-05-11 10:55:56 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
2026-05-13 16:27:54 -05:00
|
|
|
token.vs.SetChar(pos++, text[m_pos]);
|
2026-05-11 10:55:56 -05:00
|
|
|
if(pos >= reserve)
|
|
|
|
|
{
|
|
|
|
|
reserve <<= 1;
|
2026-05-13 16:27:54 -05:00
|
|
|
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
|