846 lines
20 KiB
MQL5
846 lines
20 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 CJsonParser
| |
{
| |
private:
| |
ITsnJsonParserStream* m_in;
| |
bool m_del_caller;
| |
| |
//---
| |
uchar m_prev_c;
| |
uint8_t m_in_n;
| |
long m_mask;
| |
long m_last_number;
| |
double m_last_number_dbl;
| |
| |
//----
| |
long m_sax_frac_int;
| |
ushort m_sax_frac_digits;
| |
uchar m_ch;
| |
| |
public:
| |
CJsonParserStream(void) : m_del_caller(false) {}
| |
~CJsonParserStream(void);
| |
| |
//---
| |
__forceinline int CurrentStack() const { return m_stak_curr + 1; }
| |
__forceinline int CurrentStackState() const { return m_stak_curr_state; }
| |
void KillParse();
| |
| |
//---
| |
void SetCaller(ITsnJsonParserStream* caller, bool del);
| |
void CheckLimpieza();
| |
| |
//---
| |
bool ParseSreamSax();
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
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 m_next=TSN_JSON_TOK_INVALID;
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void CJsonParserStream::KillParse(void)
| |
{
| |
JSONPARSERBYLEO_SAX_ON_FINISH
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
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 = 0; // Reiniciamos el pos
| |
// 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..
| |
}
| |
| |
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
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_number = 0;
| |
m_mask = 0;
| |
//dbl
| |
m_in_n = 0;
| |
// extra
| |
m_stak_pos[JSONPARSERBYLEO_SAX_STR_START] = -1; // string y keys
| |
| |
//---
| |
m_in.OnStartDocument();
| |
| |
//--- Inicial
| |
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_pos++;
| |
while(true)
| |
{
| |
const uchar _f__c = m_raw[m_pos];
| |
if(_f__c < 33)
| |
{
| |
m_pos++;
| |
continue;
| |
}
| |
| |
//---
| |
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_INVALID;
| |
}
| |
break;
| |
}
| |
}
| |
}
| |
| |
//--- Bucle
| |
while(true)
| |
{
| |
switch(m_next)
| |
{
| |
case TSN_JSON_TOK_NUMBER_NEG:
| |
{
| |
//---
| |
// m_last_number=0 ya viene a 0
| |
m_mask = -1; // solo cambiamos la mascara
| |
m_next = TSN_JSON_TOK_NUMBER;
| |
| |
//---
| |
if(++m_pos >= m_len) // Ahora en numero de lo contraio a la siguiente..
| |
return true;
| |
break;
| |
}
| |
| |
//---
| |
case TSN_JSON_TOK_NUMBER:
| |
{
| |
//---
| |
m_ch = m_raw[m_pos] ^ '0';
| |
| |
//--- bucle principal
| |
while(true)
| |
{
| |
m_last_number = (m_last_number << 3) + (m_last_number << 1) + m_ch;
| |
m_pos++;
| |
if(m_pos >= m_len)
| |
return true;
| |
m_ch = m_raw[m_pos] ^ '0';
| |
if(m_ch < 10)
| |
continue;
| |
else
| |
if(m_ch == 30 || (m_ch | 0x20) == 117)
| |
{
| |
m_prev_c = m_ch;
| |
m_last_number_dbl = double(m_last_number);
| |
m_next = TSN_JSON_TOK_SAX_DBL;
| |
// dbl ya reincia ..
| |
break;
| |
}
| |
else
| |
{
| |
m_in.OnInteger((m_last_number ^ m_mask) - m_mask);
| |
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY;
| |
// reiniciamos
| |
m_last_number = 0;
| |
m_mask = 0;
| |
break;
| |
}
| |
}
| |
| |
//--- no hago el m_pos++ dado que ahora mismo estamo en el seg char
| |
break;
| |
}
| |
| |
//---
| |
case TSN_JSON_TOK_STRING:
| |
{
| |
//---
| |
//TSN_JSON_RESERVAR(2)
| |
| |
//---
| |
if(m_stak_pos[JSONPARSERBYLEO_SAX_STR_START] == -1)
| |
{
| |
m_stak_pos[JSONPARSERBYLEO_SAX_STR_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_stak_pos[JSONPARSERBYLEO_SAX_STR_START];
| |
m_in.OnString(start, m_pos - start);
| |
| |
//---
| |
m_stak_pos[JSONPARSERBYLEO_SAX_STR_START] = -1;
| |
| |
//---
| |
m_pos++;
| |
| |
//---
| |
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY;
| |
break;
| |
}
| |
| |
//---
| |
m_pos++;
| |
}
| |
| |
| |
//---
| |
// No enconto nada.. entonces.. sale noma vovemos luego
| |
| |
//---
| |
break;
| |
}
| |
case TSN_JSON_TOK_BOOL_TRUE:
| |
{
| |
//TSN_JSON_RESERVAR(1)
| |
// t r u e W
| |
// [-4] [-3] [-2] [-1] [0]
| |
| |
//---
| |
if(m_pos + 5 < m_len)
| |
{
| |
m_pos += 4; // Aumentamos y continuamos
| |
m_in.OnBool(true);
| |
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY;
| |
}
| |
break;
| |
}
| |
case TSN_JSON_TOK_BOOL_FALSE:
| |
{
| |
//TSN_JSON_RESERVAR(1)
| |
// f a l s e W
| |
// [] [-4] [-3] [-2 ] [-1] [0]
| |
| |
//---
| |
if(m_pos + 6 < m_len)
| |
{
| |
m_pos += 5; // Aumentamos y continuamos
| |
m_in.OnBool(false);
| |
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY;
| |
}
| |
break;
| |
}
| |
case TSN_JSON_TOK_NULL:
| |
{
| |
//TSN_JSON_RESERVAR(1)
| |
// n u l l W
| |
// [-4][-3][-2][-1][]
| |
| |
| |
//---
| |
// Solo si hay 5 chars mas si no no hacemo snada
| |
if(m_pos + 5 < m_len)
| |
{
| |
m_pos += 4; // Aumentamos y continuamos
| |
m_in.OnNull();
| |
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY;
| |
}
| |
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
| |
{
| |
m_in.OnEndDocument();
| |
JSONPARSERBYLEO_SAX_ON_FINISH
| |
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
| |
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_stak_pos[JSONPARSERBYLEO_SAX_STR_START] == -1)
| |
{
| |
m_stak_pos[JSONPARSERBYLEO_SAX_STR_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_stak_pos[JSONPARSERBYLEO_SAX_STR_START];
| |
m_in.OnKey(start, m_pos - start);
| |
| |
//---
| |
m_stak_pos[JSONPARSERBYLEO_SAX_STR_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
| |
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)
| |
{
| |
JSONPARSERBYLEO_SAX_ON_FINISH
| |
m_last_err = TSN_JSON_ERR_MALFORDMED_JSON_AFTER_VAL;
| |
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)
| |
{
| |
JSONPARSERBYLEO_SAX_ON_FINISH
| |
m_last_err = TSN_JSON_ERR_MALFORDMED_JSON_AFTER_COR_INI;
| |
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
| |
return false;
| |
}
| |
break;
| |
}
| |
break;
| |
}
| |
| |
//---
| |
case TSN_JSON_TOK_SAX_DBL:
| |
{
| |
// Comentarios para que no te pierdas
| |
// En si la tenica a usar es como ir lockeradno o teneinedo estaods con reuso de varialbes
| |
// m_prev_c continee el ultimo char.. lo uso priemro para poder evitar una lectura de array mas
| |
// y luego lo utilizo mas adalante para no vover a ver si m_pos e E\e
| |
// Dado que podria no entrar si eso pasa
| |
// Ahora otras vairalevbs presents son m_in_n esta es como mi avarialbe de estado
| |
// La uso para ver en que fase estoy
| |
// Luego tmabien se reusa m_last_number en el E\e para ver si tenia signo o no
| |
// Aqui si neceisto persistencia.. por eso reutilizo esa varialbe..
| |
if(m_prev_c == 30)
| |
{
| |
if(m_in_n == 0) // etapa inicial 0
| |
{
| |
m_in_n++; // 1
| |
m_pos++;
| |
m_sax_frac_int = 0;
| |
m_sax_frac_digits = 0;
| |
}
| |
| |
//---
| |
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_last_number_dbl += m_sax_frac_int * g_Exp10[m_sax_frac_digits];
| |
break;
| |
}
| |
| |
//---
| |
m_sax_frac_int = (m_sax_frac_int << 3) + (m_sax_frac_int << 1) + m_ch;
| |
m_sax_frac_digits++;
| |
m_pos++;
| |
}
| |
| |
//---
| |
// Pasamos con un char extra..
| |
}
| |
| |
//--- Ahora procesamos E
| |
if(m_prev_c == 15 || (m_ch | 0x20) == 117) // e or E
| |
{
| |
//---
| |
switch(m_in_n)
| |
{
| |
case 0:
| |
{
| |
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
| |
m_last_number = 1; // bool exp_sign = true;
| |
| |
//---
| |
m_ch = m_raw[m_pos] ^ '0';
| |
if(m_ch > 9)
| |
{
| |
if(m_ch == ('-' ^ '0'))
| |
m_last_number = 0;
| |
| |
//---
| |
m_ch = UCHAR_MAX;
| |
| |
//---
| |
if(++m_pos >= m_len)
| |
return true;
| |
}
| |
| |
//---
| |
// Cae al siguiente
| |
}
| |
| |
case 2:
| |
{
| |
m_in_n++; // 3
| |
m_sax_frac_int = 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)
| |
{
| |
m_last_number_dbl *= m_last_number ? g_Pow10[m_sax_frac_int] : g_Exp10[m_sax_frac_int];
| |
break;
| |
}
| |
| |
//---
| |
m_sax_frac_int = (m_sax_frac_int << 3) + (m_sax_frac_int << 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';
| |
}
| |
break; // Sale esta vez si..
| |
}
| |
| |
//---
| |
default:
| |
{
| |
JSONPARSERBYLEO_SAX_ON_FINISH
| |
m_last_err = TSN_JSON_ERR_INVALID_STATE_IN_PARSE_DOUBLE_E;
| |
return false;
| |
}
| |
}
| |
}
| |
| |
//--- Reinicio base
| |
m_in_n = 0;
| |
| |
//---
| |
TsnBitInterprete bit;
| |
bit.double_value = m_last_number_dbl;
| |
bit.long_value ^= (m_mask & (1LL << 63));
| |
m_in.OnReal(bit.double_value);
| |
| |
//---
| |
m_last_number = 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;
| |
}
| |
| |
//---
| |
default:
| |
{
| |
m_last_err = TSN_JSON_ERR_INVALID_CHAR;
| |
JSONPARSERBYLEO_SAX_ON_FINISH
| |
return false;
| |
}
| |
}
| |
}
| |
| |
//---
| |
return true;
| |
}
| |
}
| |
#endif // JSONPARSERBYLEO_SRC_JSONSAXIMP_MQH
| |
//+------------------------------------------------------------------+
|