JsonParserByLeo/Src/JsonSaxImp.mqh

833 行
20 KiB
MQL5
Raw パーマリンク 通常表示 履歴

2026-08-04 08:01:43 -05:00
//+------------------------------------------------------------------+
//| 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)
2026-08-04 09:07:11 -05:00
/* Notas de mi parte
Siempre cuando vuevea a llamar aseungramtne de agregar datos amenos un solo dato es lo minimo..
*/
2026-08-04 08:01:43 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CJsonParserStream : public CJsonParser
{
private:
ITsnJsonParserStream* m_in;
2026-08-04 09:02:31 -05:00
bool m_del_caller;
//---
2026-08-04 08:01:43 -05:00
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;
int m_sax_frac_digits;
public:
2026-08-04 09:02:31 -05:00
CJsonParserStream(void) : m_del_caller(false) {}
~CJsonParserStream(void);
2026-08-04 08:01:43 -05:00
//---
2026-08-04 09:02:31 -05:00
__forceinline int CurrentStack() const { return m_stak_curr + 1; }
__forceinline int CurrentStackState() const { return m_stak_curr_state; }
2026-08-04 08:01:43 -05:00
void KillParse();
2026-08-04 09:02:31 -05:00
//---
void SetCaller(ITsnJsonParserStream* caller, bool del);
void CheckLimpieza();
2026-08-04 08:01:43 -05:00
//---
bool ParseSreamSax();
};
2026-08-04 09:02:31 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CJsonParserStream::~CJsonParserStream(void)
{
if(m_del_caller && CheckPointer(m_in) == POINTER_DYNAMIC)
delete m_in;
}
2026-08-04 08:01:43 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// 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
}
2026-08-04 09:02:31 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
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..
}
2026-08-04 08:01:43 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CJsonParserStream::ParseSreamSax(void)
{
//--- Inicial
if(m_next == TSN_JSON_TOK_INVALID)
{
// general
2026-08-04 08:01:43 -05:00
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
2026-08-04 08:01:43 -05:00
m_stak_pos[JSONPARSERBYLEO_SAX_STR_START] = -1; // string y keys
2026-08-04 09:02:31 -05:00
//---
m_in.OnStartDocument();
2026-08-04 08:01:43 -05:00
//--- 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:
2026-08-04 08:01:43 -05:00
{
//---
// m_last_number=0 ya viene a 0
m_mask = -1; // solo cambiamos la mascara
m_next = TSN_JSON_TOK_NUMBER;
2026-08-04 08:01:43 -05:00
//---
if(++m_pos >= m_len) // Ahora en numero de lo contraio a la siguiente..
return true;
break;
}
2026-08-04 08:01:43 -05:00
//---
case TSN_JSON_TOK_NUMBER:
{
2026-08-04 08:01:43 -05:00
//---
uchar ch = m_raw[m_pos] ^ '0';
2026-08-04 08:01:43 -05:00
//--- bucle principal
2026-08-04 08:01:43 -05:00
while(true)
{
m_last_number = (m_last_number << 3) + (m_last_number << 1) + ch;
m_pos++;
if(m_pos >= m_len)
return true;
2026-08-04 08:01:43 -05:00
ch = m_raw[m_pos] ^ '0';
if(ch < 10)
continue;
else
if(ch == 30 || (ch | 0x20) == 117)
2026-08-04 08:01:43 -05:00
{
m_prev_c = ch;
m_last_number_dbl = double(m_last_number);
m_next = TSN_JSON_TOK_SAX_DBL;
// dbl ya reincia ..
2026-08-04 08:01:43 -05:00
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;
2026-08-04 08:01:43 -05:00
}
}
//--- 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
2026-08-04 09:02:31 -05:00
while(true)
2026-08-04 08:01:43 -05:00
{
2026-08-04 09:02:31 -05:00
//---
if(m_pos >= m_len)
{
return true;
}
2026-08-04 08:01:43 -05:00
//---
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
2026-08-04 09:29:06 -05:00
m_in.OnBool(true);
2026-08-04 08:01:43 -05:00
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
2026-08-04 09:29:06 -05:00
m_in.OnBool(false);
2026-08-04 08:01:43 -05:00
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++;
2026-08-04 09:02:31 -05:00
m_stak_state[m_stak_curr] = TSN_JSON_CTX_IN_OBJ;
m_stak_curr_state = TSN_JSON_CTX_IN_OBJ;
2026-08-04 08:01:43 -05:00
//---
m_pos++;
//---
m_next = TSN_JSON_TOK_SAX_LLAVE_INI;
//---
break;
}
case TSN_JSON_TOK_COR_INI:
{
//_--
m_in.OnStartArr();
//---
m_stak_curr++;
2026-08-04 09:02:31 -05:00
m_stak_state[m_stak_curr] = TSN_JSON_CTX_IN_ARR;
m_stak_curr_state = TSN_JSON_CTX_IN_ARR;
2026-08-04 08:01:43 -05:00
//---
m_pos++;
//---
m_next = TSN_JSON_TOK_SAX_COR_INI;
//---
break;
}
case TSN_JSON_TOK_LLAVE_END:
{
2026-08-04 09:02:31 -05:00
//---
m_stak_curr--;
2026-08-04 08:01:43 -05:00
//---
m_in.OnEndObJ();
//---
2026-08-04 09:02:31 -05:00
if(m_stak_curr < 0) // termino
2026-08-04 08:01:43 -05:00
{
m_in.OnEndDocument();
JSONPARSERBYLEO_SAX_ON_FINISH
return true;
}
2026-08-04 09:02:31 -05:00
//---
m_stak_curr_state = m_stak_state[m_stak_curr];
2026-08-04 08:01:43 -05:00
//---
m_pos++;
//---
m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY;
//---
break;
}
case TSN_JSON_TOK_COR_END:
{
2026-08-04 09:02:31 -05:00
//---
m_stak_curr--;
2026-08-04 08:01:43 -05:00
//---
m_in.OnEndArr();
//---
2026-08-04 09:02:31 -05:00
if(m_stak_curr < 0) // termino
2026-08-04 08:01:43 -05:00
{
m_in.OnEndDocument();
JSONPARSERBYLEO_SAX_ON_FINISH
return true;
}
2026-08-04 09:02:31 -05:00
//---
m_stak_curr_state = m_stak_state[m_stak_curr];
2026-08-04 08:01:43 -05:00
//---
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
2026-08-04 09:02:31 -05:00
while(true)
2026-08-04 08:01:43 -05:00
{
2026-08-04 09:02:31 -05:00
//---
if(m_pos >= m_len)
{
return true;
}
2026-08-04 08:01:43 -05:00
//---
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;
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++;
}
2026-08-04 09:02:31 -05:00
//---
break;
2026-08-04 08:01:43 -05:00
}
//---
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;
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;
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
2026-08-04 08:01:43 -05:00
{
m_in_n++; // 1
2026-08-04 08:01:43 -05:00
m_pos++;
m_sax_frac_int = 0;
m_sax_frac_digits = 0;
}
//---
while(true)
{
//---
if(m_pos >= m_len)
return true;
//---
const uchar c = m_raw[m_pos] ^ '0';
if(c > 9)
{
m_in_n = 0; // Vuelta en 0 (ahora para procesar lod giitos)
2026-08-04 08:01:43 -05:00
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) + c;
m_sax_frac_digits++;
m_pos++;
}
//---
// Pasamos con un char extra..
}
//--- Ahora procesamos E
if(m_prev_c == 15 || (m_raw[m_pos] | 0x20) == 'e') // e or E
{
//---
switch(m_in_n)
{
case 0:
2026-08-04 08:01:43 -05:00
{
m_in_n++; // 1
2026-08-04 08:01:43 -05:00
m_prev_c = 15;
m_pos++;
if(m_pos >= m_len)
return true; // Salimos
// Cae al siguiente
}
case 1:
2026-08-04 08:01:43 -05:00
{
m_in_n++; // 2
2026-08-04 08:01:43 -05:00
m_last_number = 1; // bool exp_sign = true;
//---
const uchar _c = m_raw[m_pos];
if(_c == '-')
{
m_last_number = 0; // exp_sign = false;
m_pos++;
}
else
if(_c == '+')
{
m_pos++;
}
//---
if(m_pos >= m_len)
return true;
// Cae al siguiente
}
case 2:
2026-08-04 08:01:43 -05:00
{
m_in_n++; // 3
2026-08-04 08:01:43 -05:00
m_sax_frac_int = 0;
// Cae al siguiente
}
case 3:
2026-08-04 08:01:43 -05:00
{
while(true)
{
const uchar _c = m_raw[m_pos] ^ '0';
if(_c < 9)
{
m_last_number_dbl *= m_last_number ? g_Pow10[m_sax_frac_int] : g_Exp10[m_sax_frac_int];
2026-08-04 08:01:43 -05:00
break;
}
//---
m_sax_frac_int = (m_sax_frac_int << 3) + (m_sax_frac_int << 1) + _c;
//---
m_pos++;
if(m_pos >= m_len)
return true;
}
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
2026-08-04 08:01:43 -05:00
m_in_n = 0;
//---
2026-08-28 08:28:32 -05:00
TsnBitInterprete bit;
2026-08-04 08:01:43 -05:00
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
2026-08-04 08:01:43 -05:00
//---
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] == ':')
2026-08-04 09:02:31 -05:00
{
//---
m_pos++; // Luego del
//--- Buscamos valor
m_next = TSN_JSON_TOK_SAX_COM_KEY;
2026-08-04 08:01:43 -05:00
break;
2026-08-04 09:02:31 -05:00
}
2026-08-04 08:01:43 -05:00
//---
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
//+------------------------------------------------------------------+