JsonParserByLeo/Src/JsonSaxImp.mqh
Nique_372 978529bcc4
2026-10-03 22:25:05 -05:00

904 lines
24 KiB
MQL5

//+------------------------------------------------------------------+
//| JsonSaxImp.mqh |
//| Copyright 2026, Niquel Mendoza |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Niquel Mendoza"
#property link "https://www.mql5.com"
#property strict
#ifndef JSONPARSERBYLEO_SRC_JSONSAXIMP_MQH
#define JSONPARSERBYLEO_SRC_JSONSAXIMP_MQH
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#include "JsonParser.mqh"
#include "JsonSax.mqh"
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
namespace TSN
{
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#define JSONPARSERBYLEO_SAX_STR_START (0)
/* Notas de mi parte
Siempre cuando vuevea a llamar aseungramtne de agregar datos amenos un solo dato es lo minimo..
*/
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CJsonParserStream : public CBaseLanFormat
{
private:
//--- Del parsing en si
ushort m_stak_state[JSONPARSERBYLEO_MAX_STACK];
int m_stak_curr;
ushort m_stak_curr_state;
//---
ENUM_TSN_JSON_LAST_ERR m_last_err;
//--- hook
ITsnJsonParserStream* m_in;
bool m_del_caller;
//--- parsing de numeros
uint8_t m_in_n; // estado en decimal parse
long m_last_i; // ultimo numero entero
long m_mask; // mascara (pos o neg)
// estas se resetean solas
int m_sax_frac_dc; // incio del dc \ si el ex epo luego del E\e es neg
int m_sax_frac_expo; // esponente del numero rea
int m_total_dig; // total de digitos de la aprte entera y fraccionaria
uchar m_ch; // caracter actual
uchar m_fci; // primer caracter numerico (Esto para el lz y ov)
int m_sax_frac_exponente_f; // exponente parse (fuill numero luego del E\e)
uchar m_prev_c; // loceo
//--- start (inicio de string o \ numero )
int m_start;
//--- fijos (true\false\null)
uint m_tok_fixed; // token actual (max 4bytes)
uchar m_sc; // sh actual (empieza en 0)
public:
CJsonParserStream(void);
~CJsonParserStream(void);
//---
__forceinline ENUM_TSN_JSON_LAST_ERR LastErr() const { return m_last_err; }
//---
__forceinline int CurrentStack() const { return m_stak_curr + 1; }
__forceinline ushort CurrentStackState() const { return m_stak_curr_state; }
void KillParse();
//---
void SetCaller(ITsnJsonParserStream* caller, bool del);
void CheckLimpieza();
//---
bool ParseSreamSax();
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CJsonParserStream::CJsonParserStream(void)
: m_del_caller(false)
{
m_next = TSN_JSON_TOK_INVALID;
m_pos = 0; // offset inicial
}
//+------------------------------------------------------------------+
CJsonParserStream::~CJsonParserStream(void)
{
if(m_del_caller && CheckPointer(m_in) == POINTER_DYNAMIC)
delete m_in;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// Al terminar solo lo poneres en tok invalid..
//---
#define JSONPARSERBYLEO_SAX_ON_FINISH_P m_next=TSN_JSON_TOK_INVALID;
#define JSONPARSERBYLEO_SAX_ON_FINISH_CORR \
m_in.OnEndDocument(); \
JSONPARSERBYLEO_SAX_ON_FINISH_P
//---
#define JSONPARSERBYLEO_SAX_ON_FINISH_ERR \
m_in.OnError(); \
JSONPARSERBYLEO_SAX_ON_FINISH_P
//---
#define JSONPARSERBYLEO_SAX_ITER_INT \
STRCONV_TOV_ALG(m_last_i, m_ch) \
if(++m_pos >= m_len) \
return true; \
if((m_ch = m_raw[m_pos] ^ '0') > 9) \
break; \
//---
#define JSONPARSERBYLEO_SAX_ITER_INT_UNROLLED_8 \
JSONPARSERBYLEO_SAX_ITER_INT \
JSONPARSERBYLEO_SAX_ITER_INT \
JSONPARSERBYLEO_SAX_ITER_INT \
JSONPARSERBYLEO_SAX_ITER_INT \
JSONPARSERBYLEO_SAX_ITER_INT \
JSONPARSERBYLEO_SAX_ITER_INT \
JSONPARSERBYLEO_SAX_ITER_INT \
JSONPARSERBYLEO_SAX_ITER_INT
//---
#define JSONPARSERBYLEO_SAX_ITER_INT_SKIP \
while(true) \
{ \
if(++m_pos >= m_len) \
return true; \
if((m_ch = m_raw[m_pos] ^ '0') > 9) \
break; \
} \
//---
#define JSONPARSERBYLEO_SAX_ITER_INT_UNROLLED_19 \
JSONPARSERBYLEO_SAX_ITER_INT_UNROLLED_8 \
JSONPARSERBYLEO_SAX_ITER_INT_UNROLLED_8 \
JSONPARSERBYLEO_SAX_ITER_INT \
JSONPARSERBYLEO_SAX_ITER_INT \
JSONPARSERBYLEO_SAX_ITER_INT
//---
#define JSONSAX_IMP_FIXED(TOKS_NUM, VALID, ERROR, EVENT) \
while(++m_pos < m_len && m_sc < ((TOKS_NUM) << 3)) \
{ \
m_tok_fixed |= (uint)m_raw[m_pos] << (m_sc); \
m_sc += 8; \
} \
if(m_sc == ((TOKS_NUM) << 3)) \
{ \
if(m_tok_fixed != VALID) \
{ \
m_last_err=ERROR; \
JSONPARSERBYLEO_SAX_ON_FINISH_ERR \
return false; \
} \
EVENT; \
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY; \
m_sc = 0; \
m_tok_fixed = 0; \
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CJsonParserStream::KillParse(void)
{
m_next = TSN_JSON_TOK_INVALID; // sin nada en medio
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CJsonParserStream::SetCaller(ITsnJsonParserStream *caller, bool del)
{
m_del_caller = del;
m_in = caller;
m_in.MainPointer(&this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CJsonParserStream::CheckLimpieza(void)
{
// Revisamos el estado previo
// La idea ees ver si estamos en string SI no lo estabamos entonces es seguro retrazar el pos
if(((1 << m_next) & (TSN_JSON_F_KEY | TSN_JSON_F_STRING)) == 0)
{
m_pos = m_offset; // Reiniciamos el pos
m_len = 0; // toca llenar dado que ahora es 0
// Entonces la idea es que la siguiente llama el que llame a esta clase
// Llente el array auqneusea conreserva pero hasta m_len
// Lo deal seria ejeuctar esto antes de cada relleneado
}
// No se puede..
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// NOTA minimo intrudcion un byte.. por cada calling no menos...
bool CJsonParserStream::ParseSreamSax(void)
{
//--- Inicial
if(m_next == TSN_JSON_TOK_INVALID)
{
//---
// general
m_stak_curr = -1;
m_last_err = TSN_JSON_NOT_ERR;
m_pos = m_offset;
// numeros
m_last_i = 0;
m_mask = 0;
//dbl
m_in_n = 0;
// extra
m_start = -1; // string y keys
// fijos
m_tok_fixed = 0;
m_sc = 0;
//---
m_in.OnStartDocument();
//--- Inicial
//Print(m_pos);
const uchar _f_c = m_raw[m_pos];
if(_f_c == '{')
{
m_next = TSN_JSON_TOK_LLAVE_INI;
}
else
if(_f_c == '[')
{
m_next = TSN_JSON_TOK_COR_INI;
}
else
{
m_next = TSN_JSON_TOK_SAX_SLOW_PATH_FIND;
if(++m_pos >= m_len)
return true; // falta
}
}
//--- Bucle
while(true)
{
switch(m_next)
{
case TSN_JSON_TOK_NUMBER_NEG:
{
//---
// m_last_i=0 ya viene a 0
m_mask = -1; // solo cambiamos la mascara
m_start = m_pos;
//---
if(++m_pos >= m_len) // Ahora en numero de lo contraio a la siguiente..}
{
m_next = TSN_JSON_TOK_NUMBER;
return true;
}
// Si hay aun valor cae aqui...
}
//---
case TSN_JSON_TOK_NUMBER:
{
//---
m_ch = m_raw[m_pos] ^ '0';
//---
if(m_start == -1)
{
m_start = m_pos;
m_fci = m_ch;
}
//---
while(m_ch < 10) // solo si es menor a 10
{
JSONPARSERBYLEO_SAX_ITER_INT_UNROLLED_19
//--- aca ya no tiene sentido acomular ov seguro .... asi que saltamos noma
JSONPARSERBYLEO_SAX_ITER_INT_SKIP
break;
}
//--- lz
// si el valor inicial es 0 pero .. por alguna razon hay dos digitos que se procesaron...
m_total_dig = (m_pos - m_start + int(m_mask));
if(m_fci == 0 && m_total_dig > 1)
{
m_last_err = TSN_JSON_ERR_LEADING_ZEROS;
JSONPARSERBYLEO_SAX_ON_FINISH_ERR
return false;
}
//--- final
if(STRCONV_DECX_CH_IS_DEC(m_ch))
{
m_prev_c = m_ch;
m_next = TSN_JSON_TOK_SAX_DBL;
}
else
{
if(m_total_dig > 19 || m_last_i < 0) // overflow
m_in.OnBigInteger(m_start, m_total_dig);
else
m_in.OnInteger((m_last_i ^ m_mask) - m_mask);
//--- terminamos
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY;
m_last_i = 0;
m_mask = 0;
m_start = -1;
}
//--- no hago el m_pos++ dado que ahora mismo estamo en el seg char
break;
}
//---
case TSN_JSON_TOK_STRING:
{
//---
if(m_start == -1)
m_start = ++m_pos;
//--- Char por char
while(true)
{
//---
if(m_pos >= m_len)
return true;
//---
const uchar c = m_raw[m_pos];
//---
if(c == '\\')
{
m_pos += 2;
continue;
}
if(c == '"')
{
//---
const int start = m_start;
m_in.OnString(start, m_pos - start);
//---
m_start = -1;
m_pos++;
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY;
break;
}
//---
m_pos++;
}
//---
break;
}
case TSN_JSON_TOK_BOOL_TRUE:
{
//--- el primer t ya se cuenta como valido solo los isuigntes
JSONSAX_IMP_FIXED(3, TSNJSON_VAL_TRUE, TSN_JSON_ERR_MALFORMED_TRUE, m_in.OnBool(true))
break;
}
case TSN_JSON_TOK_BOOL_FALSE:
{
//---
JSONSAX_IMP_FIXED(4, TSNJSON_VAL_FALSE, TSN_JSON_ERR_MALFORMED_FALSE, m_in.OnBool(false))
break;
}
case TSN_JSON_TOK_NULL:
{
//---
JSONSAX_IMP_FIXED(3, TSNJSON_VAL_NULL, TSN_JSON_ERR_MALFORMED_NULL, m_in.OnNull())
break;
}
case TSN_JSON_TOK_COMMA:
{
//---
m_pos++;
//---
if(m_stak_curr_state == TSN_JSON_CTX_IN_OBJ)
m_next = TSN_JSON_TOK_SAX_FIND_KEY;
else
m_next = TSN_JSON_TOK_SAX_COM_KEY;
break;
}
case TSN_JSON_TOK_LLAVE_INI:
{
//_--
m_in.OnStartObj();
//---
m_stak_curr++;
m_stak_state[m_stak_curr] = TSN_JSON_CTX_IN_OBJ;
m_stak_curr_state = TSN_JSON_CTX_IN_OBJ;
//---
m_pos++;
//---
m_next = TSN_JSON_TOK_SAX_LLAVE_INI;
//---
break;
}
case TSN_JSON_TOK_COR_INI:
{
//_--
m_in.OnStartArr();
//---
m_stak_curr++;
m_stak_state[m_stak_curr] = TSN_JSON_CTX_IN_ARR;
m_stak_curr_state = TSN_JSON_CTX_IN_ARR;
//---
m_pos++;
m_next = TSN_JSON_TOK_SAX_COR_INI;
//---
break;
}
case TSN_JSON_TOK_LLAVE_END:
{
//---
m_stak_curr--;
//---
m_in.OnEndObJ();
//---
if(m_stak_curr < 0) // termino
{
JSONPARSERBYLEO_SAX_ON_FINISH_CORR
return true;
}
//---
m_stak_curr_state = m_stak_state[m_stak_curr];
m_pos++;
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY;
//---
break;
}
case TSN_JSON_TOK_COR_END:
{
//---
m_stak_curr--;
//---
m_in.OnEndArr();
//---
if(m_stak_curr < 0) // termino
{
m_in.OnEndDocument();
JSONPARSERBYLEO_SAX_ON_FINISH_CORR
return true;
}
//---
m_stak_curr_state = m_stak_state[m_stak_curr];
m_pos++;
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY;
//---
break;
}
case TSN_JSON_TOK_KEY:
{
//---
if(m_start == -1)
m_start = ++m_pos;
//--- Char por char
while(true)
{
//---
if(m_pos >= m_len)
return true;
//---
const uchar c = m_raw[m_pos];
if(c == '\\')
{
m_pos += 2;
continue;
}
if(c == '"')
{
//---
const int start = m_start;
m_in.OnKey(start, m_pos - start);
//---
m_start = -1;
m_pos++;
m_next = TSN_JSON_TOK_SAX_DOS_PUNTOS;
break;
}
m_pos++;
}
//---
break;
}
case TSN_JSON_TOK_INVALID:
{
m_last_err = TSN_JSON_ERR_INVALID_CHAR;
JSONPARSERBYLEO_SAX_ON_FINISH_ERR
return false;
}
case TSN_JSON_TOK_SAX_NO_NEXT_KEY:
{
while(true)
{
//---
if(m_pos >= m_len)
return true; // esperando mas ...
//---
const uchar _c = m_raw[m_pos];
if(_c < 33)
{
m_pos++;
continue;
}
//---
m_next = g_table_json_tokens[_c];
if(((1 << m_next) & m_stak_curr_state) == 0)
{
m_last_err = TSN_JSON_ERR_MALFORDMED_JSON_AFTER_VAL;
JSONPARSERBYLEO_SAX_ON_FINISH_ERR
return false;
}
break;
}
break;
}
//---
case TSN_JSON_TOK_SAX_LLAVE_INI:
{
while(true)
{
//---
if(m_pos >= m_len)
return true; // esperando mas ...
//---
const uchar _c = m_raw[m_pos];
if(_c == '"')
{
m_next = TSN_JSON_TOK_KEY;
break;
}
if(_c == '}')
{
m_next = TSN_JSON_TOK_LLAVE_END;
break;
}
m_pos++;
}
break;
}
//---
case TSN_JSON_TOK_SAX_COR_INI:
{
while(true)
{
//---
if(m_pos >= m_len)
return true; // esperando mas ...
//---
const uchar _c = m_raw[m_pos];
if(_c < 33)
{
m_pos++;
continue;
}
m_next = g_table_json_tokens[_c];
if(((1 << m_next) & TSN_JSON_F_VAL_COR) == 0)
{
m_last_err = TSN_JSON_ERR_MALFORDMED_JSON_AFTER_COR_INI;
JSONPARSERBYLEO_SAX_ON_FINISH_ERR
return false;
}
break;
}
break;
}
case TSN_JSON_TOK_SAX_FIND_KEY:
{
while(true)
{
//---
if(m_pos >= m_len)
return true; // esperando mas ...
//---
if(m_raw[m_pos] == '"')
{
m_next = TSN_JSON_TOK_KEY;
break;
}
m_pos++;
}
break;
}
case TSN_JSON_TOK_SAX_COM_KEY:
{
while(true)
{
//---
if(m_pos >= m_len)
return true; // esperando mas ...
//---
const uchar _c = m_raw[m_pos];
if(_c < 33)
{
m_pos++;
continue;
}
m_next = g_table_json_tokens[_c];
if(((1 << m_next) & TSN_JSON_F_ALL_I_VAL) == 0)
{
m_last_err = TSN_JSON_ERR_MALFORDMED_JSON_FIND_VAL;
JSONPARSERBYLEO_SAX_ON_FINISH_ERR
return false;
}
break;
}
break;
}
//---
case TSN_JSON_TOK_SAX_DBL:
{
//---
if(m_prev_c == 30)
{
if(m_in_n == 0) // etapa inicial 0
{
m_in_n++; // 1
m_pos++;
m_sax_frac_expo = 0; // exponenet actual
m_sax_frac_dc = m_pos; // inicio de la parte dc imal
}
//---
while(true)
{
//---
if(m_pos >= m_len)
return true;
//---
m_ch = m_raw[m_pos] ^ '0';
if(m_ch > 9)
{
m_in_n = 0; // Vuelta en 0 (ahora para procesar lod giitos)
m_prev_c = 0; // Ahora ya no podra ingresar a este if
//---
m_sax_frac_dc = (m_pos - m_sax_frac_dc);
//---
m_sax_frac_expo -= m_sax_frac_dc;
m_total_dig += m_sax_frac_dc;
break;
}
//---
m_last_i = (m_last_i << 3) + (m_last_i << 1) + m_ch;
m_pos++;
}
}
//--- Ahora procesamos E
if(m_prev_c == 15 || (m_ch | 0x20) == 117) // e or E
{
//---
switch(m_in_n)
{
case 0: // Saltamos el E\e
{
m_in_n++; // 1
m_prev_c = 15;
//---
if(++m_pos >= m_len)
return true; // Salimos
// Cae al siguiente
}
case 1:
{
m_in_n++; // 2
// por defecto positivo asi que el siguinete le resta
//---
m_sax_frac_dc = 1; // positivo
m_ch = m_raw[m_pos] ^ '0';
//---
if(m_ch > 9)
{
if(m_ch == ('-' ^ '0'))
m_sax_frac_dc = 0;
//---
m_ch = UCHAR_MAX;
//---
if(++m_pos >= m_len)
return true;
}
//---
// Cae al siguiente
}
case 2:
{
m_in_n++; // 3
m_sax_frac_exponente_f = 0;
//---
// Cae al siguiente
}
case 3:
{
//--- en caso sea uchar max (tema de signo entonces volvemos..)
if(m_ch == UCHAR_MAX)
m_ch = m_raw[m_pos] ^ '0';
//---
while(true)
{
if(m_ch > 9)
{
// si ex positivo lo sumamos.. si es negativo seria -x + -x (suma.. de negativos)
m_sax_frac_expo += m_sax_frac_dc ? m_sax_frac_exponente_f : -m_sax_frac_exponente_f;
break;
}
//---
m_sax_frac_exponente_f = (m_sax_frac_exponente_f << 3) + (m_sax_frac_exponente_f << 1) + m_ch;
//---
m_pos++;
if(m_pos >= m_len)
{
m_ch = UCHAR_MAX; // valido aun sigamos.
return true;
}
//---
m_ch = m_raw[m_pos] ^ '0'; // si guiente
}
break; // Sale esta vez si..
}
//---
default:
{
m_last_err = TSN_JSON_ERR_INVALID_STATE_IN_PARSE_DOUBLE_E;
JSONPARSERBYLEO_SAX_ON_FINISH_ERR
return false;
}
}
}
//--- Reinicio base
m_in_n = 0;
//---
if(STRCONV_DOUBLE_SAFE_DBL(m_total_dig, m_sax_frac_expo)) // limite seguro para esto
{
TsnBitInterprete bit;
bit.double_value = STRCONV_TO_DOUBLE_ALG(m_sax_frac_expo, m_last_i);
bit.long_value ^= (m_mask & (1LL << 63)); // a neg
m_in.OnReal(bit.double_value);
}
else // perdida de preciscion le damos al callbackd que decida que hacer si big decimal o lo qeu el quiera..
{
m_in.OnBigDecimal(m_start, (m_pos - m_start));
}
//---
m_start = -1; // si o si dado uqe lo guardamos
m_last_i = 0; // reiniciamos (pude haber sido 1)
m_mask = 0; // tambien a 0
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY;
break;
}
case TSN_JSON_TOK_SAX_DOS_PUNTOS:
{
//--- Find :
while(true)
{
//---
if(m_pos >= m_len)
return true; // luego
//---
if(m_raw[m_pos] == ':')
{
//---
m_pos++; // Luego del
//--- Buscamos valor
m_next = TSN_JSON_TOK_SAX_COM_KEY;
break;
}
//---
m_pos++;
}
//---
break;
}
//--- cae a slow path para contrue el inicio
case TSN_JSON_TOK_SAX_SLOW_PATH_FIND:
{
//---
m_ch = m_raw[m_pos];
while(m_ch < 33)
{
if(++m_pos >= m_len)
return true;
m_ch = m_raw[m_pos];
}
//---
if(m_ch == '}')
{
m_next = TSN_JSON_TOK_LLAVE_INI;
continue;
}
else
if(m_ch == ']')
{
m_next = TSN_JSON_TOK_COR_INI;
continue;
}
}
//--- invalid token
default:
{
m_last_err = TSN_JSON_ERR_INVALID_CHAR;
JSONPARSERBYLEO_SAX_ON_FINISH_ERR
return false;
}
}
}
//---
return true;
}
}
#endif // JSONPARSERBYLEO_SRC_JSONSAXIMP_MQH
//+------------------------------------------------------------------+