//+------------------------------------------------------------------+ //| 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_JSONSAX_JSONSAXIMP_MQH #define JSONPARSERBYLEO_SRC_JSONSAX_JSONSAXIMP_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "..\\JsonCore\\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; \ } //--- #define JSONSAX_IMP_UNROLLED_STR_ITER \ if(m_pos >= m_len) \ return true; \ if((c = m_raw[m_pos]) == '"') \ break; \ m_pos += (c == '\\') + 1; //--- #define JSONSAX_IMP_UNROLLED_STR_ITER_8 \ JSONSAX_IMP_UNROLLED_STR_ITER \ JSONSAX_IMP_UNROLLED_STR_ITER \ JSONSAX_IMP_UNROLLED_STR_ITER \ JSONSAX_IMP_UNROLLED_STR_ITER \ JSONSAX_IMP_UNROLLED_STR_ITER \ JSONSAX_IMP_UNROLLED_STR_ITER \ JSONSAX_IMP_UNROLLED_STR_ITER \ JSONSAX_IMP_UNROLLED_STR_ITER //--- #define JSONSAX_IMP_SKIPWS_I \ uchar _c=0; \ while(true) \ { \ if(m_pos >= m_len) \ return true; \ if((_c = m_raw[m_pos]) > 32) \ break; \ m_pos++; \ } //--- #define JSONSAX_IMP_FIND_CHAR_NOT_TEMP(CHAR) \ while(true) \ { \ if(m_pos >= m_len) \ return true; \ if(m_raw[m_pos] == CHAR) \ break; \ m_pos++; \ } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } } //--- 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 uchar c; while(true) { JSONSAX_IMP_UNROLLED_STR_ITER_8 JSONSAX_IMP_UNROLLED_STR_ITER_8 } //--- m_in.OnString(m_start, m_pos - m_start); //--- m_start = -1; m_pos++; m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY; 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 uchar c; while(true) { JSONSAX_IMP_UNROLLED_STR_ITER_8 JSONSAX_IMP_UNROLLED_STR_ITER_8 } //--- m_in.OnKey(m_start, m_pos - m_start); //--- m_start = -1; m_pos++; m_next = TSN_JSON_TOK_SAX_DOS_PUNTOS; 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: { //--- JSONSAX_IMP_SKIPWS_I //--- 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; } //--- case TSN_JSON_TOK_SAX_COR_INI: { //--- JSONSAX_IMP_SKIPWS_I //--- 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; } case TSN_JSON_TOK_SAX_LLAVE_INI: { //--- JSONSAX_IMP_SKIPWS_I //--- if(_c == '"') m_next = TSN_JSON_TOK_KEY; else if(_c == '}') m_next = TSN_JSON_TOK_LLAVE_END; else { m_last_err = TSN_JSON_ERR_MALFORDMED_JSON_AFTER_LLAVE_INI; JSONPARSERBYLEO_SAX_ON_FINISH_ERR return false; } break; } //--- case TSN_JSON_TOK_SAX_FIND_KEY: { //--- JSONSAX_IMP_FIND_CHAR_NOT_TEMP('"') m_next = TSN_JSON_TOK_KEY; break; } case TSN_JSON_TOK_SAX_COM_KEY: { //--- JSONSAX_IMP_SKIPWS_I //--- 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; } //--- 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 : JSONSAX_IMP_FIND_CHAR_NOT_TEMP(':') m_pos++; // Luego del : m_next = TSN_JSON_TOK_SAX_COM_KEY; break; } //--- cae a slow path para contrue el inicio case TSN_JSON_TOK_SAX_SLOW_PATH_FIND: { //--- (como tal si viene de inicla m_pos apunta aun al first char asi que no // pierde nada ahora la idea es seguir avanzado) 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; } // Cae a invalido } //--- invalid token default: { m_last_err = TSN_JSON_ERR_INVALID_CHAR; JSONPARSERBYLEO_SAX_ON_FINISH_ERR return false; } } } //--- return true; } } #endif // JSONPARSERBYLEO_SRC_JSONSAX_JSONSAXIMP_MQH //+------------------------------------------------------------------+