//+------------------------------------------------------------------+ //| Parser.mqh | //| Copyright 2026, Niquel Mendoza. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Niquel Mendoza." #property link "https://www.mql5.com" #property strict #ifndef TSNTRANSLATIONS_SRC_LANBACKEND_PARSER_MQH #define TSNTRANSLATIONS_SRC_LANBACKEND_PARSER_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "Defines.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTSNLanParser : public CBaseLanFormat { private: ENUM_TSN_JSONTRANSLATE_ERR m_last_err; uchar m_next_p; public: CTSNLanParser(void) {} ~CTSNLanParser(void) {} //--- // Parsea un json cualquier de iiudioams y genera un tempalte (plantilla) // En su tape bool ParseTemplate(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #define TSNTRANSLAN_ADD_STR m_cinta[m_cinta_pos++] = TSN_JSONTRANSLATE_VTYPE_STR; //--- #define TSNTRANSLAN_AUMENT curr_count++; //--- #define TSNTRANSLAN_ITER_KEY(H, C) \ if((C=m_raw[++m_pos])=='"') \ break; \ H ^= C; \ H *= FNV_PRIME; //--- #define TSNTRANSLAN_ITER_KEY_U16(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) \ TSNTRANSLAN_ITER_KEY(H, C) //--- // 91 >> 5 = 2 ([) 0 (&1) // 123 >> 5 = 3 ({) 1 (&1) // Lo mismo aplica para ends //--- Skip ws // Basic skip ws (con salto previo) #define TSNTRANSLATE_SWIPWS_WSKI_CURR while((_last_c = m_raw[++m_pos]) < 33) {} // Primero cachea y luego ya.. n #define TSNTRANSLATE_SWIPWS_WSKI_NONE \ _last_c = m_raw[m_pos]; \ while(_last_c < 33) { _last_c = m_raw[++m_pos]; } //--- #define TSNTRANSLATE_NEXT_NO_NEXT_KEY_P \ if(_last_c==',') \ m_next=TSN_JSONTRANSLATE_TOK_COMMA; \ else if(_last_c==curr_state) \ m_next=TSN_JSONTRANSLATE_TOK_COR_END-((_last_c>>5)&1); \ else if(TSN_JSONTRANSLATE_IS_COMMENT(_last_c)) \ { \ m_next_p=TSN_JSONTRANSLATE_TOK_EXT_NONEXT_KEY; \ m_next=TSN_JSONTRANSLATE_TOK_COMMENT_MQL; \ } \ else \ { \ m_last_err=TSN_JSONTRANSLATE_MALFORMED_FIND_VAL; \ m_next=TSN_JSONTRANSLATE_TOK_INVALID; \ } //--- Buscando un valor // Siguiente valor (cualquiera) #define TSNTRANSLATE_NEXT_VALUE(CASE) \ if(_last_c == '"') \ m_next = TSN_JSONTRANSLATE_TOK_STRING; \ else \ if((_last_c | 0x20) == '{') \ m_next = TSN_JSONTRANSLATE_TOK_COR_INI - ((_last_c>>5)&1); \ else \ if(TSN_JSONTRANSLATE_IS_COMMENT(_last_c)) \ { \ m_next_p = CASE; \ m_next = TSN_JSONTRANSLATE_TOK_COMMENT_MQL; \ } \ else \ { \ m_last_err = TSN_JSONTRANSLATE_MALFORMED_AFTER_COMMA_OBJ; \ m_next = TSN_JSONTRANSLATE_TOK_INVALID; \ } //--- Obj key // Solo key o comentario #define TSNTRANSLATE_NEXT_OBJ_INITIAL(CASE) \ if(_last_c == '"') \ m_next = TSN_JSONTRANSLATE_TOK_KEY; \ else \ if(TSN_JSONTRANSLATE_IS_COMMENT(_last_c)) \ { \ m_next_p = CASE; \ m_next = TSN_JSONTRANSLATE_TOK_COMMENT_MQL; \ } \ else \ { \ m_last_err = TSN_JSONTRANSLATE_MALFORMED_AFTER_COMMA_OBJ; \ m_next = TSN_JSONTRANSLATE_TOK_INVALID; \ } //--- Versiones completa // No next key #define TSNTRANSLATE_NEXT_NO_NEXT_KEY \ TSNTRANSLATE_SWIPWS_WSKI_NONE \ TSNTRANSLATE_NEXT_NO_NEXT_KEY_P //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTSNLanParser::ParseTemplate(void) { //--- m_last_err = TSN_JSONTRANSLATE_ERR_NOT; m_pos = m_offset; m_cinta_pos = 0; //--- uchar curr_state = 0; uchar _last_c = 0; uint curr_count = 0; uint curr_padre = 0; //--- Inicial (Solo se acpeta {) const uchar _f_c = m_raw[m_pos]; if(_f_c == '{') m_next = TSN_JSONTRANSLATE_TOK_LLAVE_INI; else // No easy { uchar __c = m_raw[++m_pos]; while(__c < 33) __c = m_raw[++m_pos]; //--- if(__c == '{') m_next = TSN_JSONTRANSLATE_TOK_LLAVE_INI; else m_next = TSN_JSONTRANSLATE_TOK_INVALID; } //--- Bucle while(true) { switch(m_next) { case TSN_JSONTRANSLATE_TOK_STRING: { //--- m_pos++; // salta " //--- Pase inicial 8 en 8 while(true) { // PrintFormat("Analizando: '%s'", CharArrayToString(m_raw, m_pos, 8)); const ulong chunk = (ulong)m_raw[m_pos] | (ulong)m_raw[m_pos + 1] << 8 | (ulong)m_raw[m_pos + 2] << 16 | (ulong)m_raw[m_pos + 3] << 24 | (ulong)m_raw[m_pos + 4] << 32 | (ulong)m_raw[m_pos + 5] << 40 | (ulong)m_raw[m_pos + 6] << 48 | (ulong)m_raw[m_pos + 7] << 56; //--- const ulong x_bs = chunk ^ TSNTABLES_SWAR_MASK_BACK_SLASH; const ulong x_qt = chunk ^ TSNTABLES_SWAR_MASK_COMILLA; ulong has_bs = TSNTABLES_SWAR_HAS(x_bs); ulong has_qt = TSNTABLES_SWAR_HAS(x_qt); //--- if((has_bs | has_qt) == 0) // Fast path { m_pos += 8; continue; } //--- if(has_bs == 0) // Si not iene bs (pero confirmamos que no es 0) (Fast path) { const ulong flag_real = TSNTABLES_BIT_DEJAR_SOLO_MINUS_SIG_BYTE(has_qt); m_pos += TSNTABLES_CTZ_64_GET_BYTE(flag_real); TSNTRANSLAN_ADD_STR m_pos++; // Luego del " break; } //--- Slow path has_bs = TSNTABLES_SWAR_HAS_EXACT(x_bs); has_qt = TSNTABLES_SWAR_HAS_EXACT(x_qt); //--- Hay bs si o si pero tambien puede haber " const uchar compacted_bs = TSNTABLES_COMPACT_UL(has_bs); const uchar prev = (((compacted_bs << 1) | TSNTABLES_MASK_IMPARES) - compacted_bs) ^ TSNTABLES_MASK_IMPARES; const uchar escaped = prev ^ compacted_bs; //--- const uchar compacted_qt = TSNTABLES_COMPACT_UL(has_qt); const uchar qt_final = compacted_qt & ~escaped; //Print("nada"); //--- if(qt_final) // Hay un qt real de cierre { //--- // Avanzamos y cerramos.. m_pos += g_tsntables_table_ctz_i8[qt_final]; TSNTRANSLAN_ADD_STR m_pos++; break; } else { m_pos += 8;// + ((prev & compacted_bs) >> 7U); // Print(CharToString(m_raw[m_pos])); // escape if(((prev & compacted_bs) >> 7U) != 0) // Overflow el ultimo char es \ y el siguiente escapa m_pos++; // Uno mas.. luego del currend } } //--- TSNTRANSLAN_AUMENT //--- TSNTRANSLATE_NEXT_NO_NEXT_KEY //--- break; } case TSN_JSONTRANSLATE_TOK_KEY: { //--- // no salto dado que igual hace ++m_pos luego ulong h = FNV_OFFSET_BASIS; while(true) { TSNTRANSLAN_ITER_KEY_U16(h, _last_c) } //--- m_cinta[m_cinta_pos++] = TSN_JSONTRANSLATE_VTYPE_KEY; m_cinta[m_cinta_pos++] = h; m_pos++; //--- Find : while(m_raw[m_pos] != ':') { m_pos++; if(m_pos >= m_len) { m_last_err = TSN_JSONTRANSLATE_EXPECTED_TWO_POINT_AFT_KEY; return false; } } //--- Buscamos valor TSNTRANSLATE_SWIPWS_WSKI_CURR TSNTRANSLATE_NEXT_VALUE(TSN_JSONTRANSLATE_TOK_EXT_NEXT_VV) //--- break; } //--- case TSN_JSONTRANSLATE_TOK_COMMA: // luego se permite un valor (inicio) { //--- TSNTRANSLATE_SWIPWS_WSKI_CURR //--- if(curr_state == TSNTRANSLAN_CTX_OBJ) { TSNTRANSLATE_NEXT_OBJ_INITIAL(TSN_JSONTRANSLATE_TOK_COMMA) } else // " [ { / { TSNTRANSLATE_NEXT_VALUE(TSN_JSONTRANSLATE_TOK_COMMA) } break; } //--- // para el ini [] // mismo formato qeu json [type(1)|cintapos(32)|curr_padre(31)] case TSN_JSONTRANSLATE_TOK_LLAVE_INI: { //--- guardar lo uqe tenaismos (numero de elementos) (+1 para contar este nuevo elemento) m_cinta[curr_padre] = curr_count + 1; //--- nuevo, ahora guardamos la direccion de nuesto padre y la nuestar (rempalzao num el por mientas) m_cinta[m_cinta_pos] = TSN_JSONTRANSLATE_VTYPE_OBJ | ulong(m_cinta_pos) << TSN_SBL_PARSE_START_CINTAPOS | ulong(curr_padre) << TSN_SBL_BIT_START_NUM_C; curr_padre = ++m_cinta_pos; // apunta al inicio + 1 (lo modificamos para count, ahora apunta aqui..) m_cinta_pos++; // salta la posicion 1 //--- reiniciamos los estados curr_state = TSNTRANSLAN_CTX_OBJ; curr_count = 0; //--- TSNTRANSLATE_SWIPWS_WSKI_CURR TSNTRANSLATE_NEXT_OBJ_INITIAL(TSN_JSONTRANSLATE_TOK_EXT_NEXT_OBJV) break; } case TSN_JSONTRANSLATE_TOK_COR_INI: { //--- guardar lo uqe tenaismos (numero de elementos) (+1 para contar este nuevo elemento) m_cinta[curr_padre] = curr_count + 1; //--- nuevo, ahora guardamos la direccion de nuesto padre y la nuestar (rempalzao num el por mientas) m_cinta[m_cinta_pos] = TSN_JSONTRANSLATE_VTYPE_ARR | ulong(m_cinta_pos) << TSN_SBL_PARSE_START_CINTAPOS | ulong(curr_padre) << TSN_SBL_BIT_START_NUM_C; curr_padre = ++m_cinta_pos; // apunta al inicio + 1 (lo modificamos para count, ahora apunta aqui..) m_cinta_pos++; // salta la posicion 1 //--- reiniciamos los estados curr_state = TSNTRANSLAN_CTX_ARR; curr_count = 0; //--- Find TSNTRANSLATE_SWIPWS_WSKI_CURR TSNTRANSLATE_NEXT_VALUE(TSN_JSONTRANSLATE_TOK_EXT_NEXT_VV) break; } //--- ends case TSN_JSONTRANSLATE_TOK_LLAVE_END: { //--- cacheamos el valor de cinta [curr_padre-1] = apunta a los datos m_cinta[curr_padre] = 1ull; // le damos su valor final const uint idata = --curr_padre; const ulong v = m_cinta[idata]; // ahora si rehacemos el valor con todo ya listo.. (curr padre apunta a la posicion correcta) m_cinta[idata] = TSN_JSONTRANSLATE_VTYPE_OBJ | ulong(curr_count) << TSNTRANSLAN_BITSTART_NUMEL | ulong(m_cinta_pos - ((v >> 1) & TSN_SBL_PARSE_MASK_CINTAPOS_S)) << TSNTRANSLAN_BITSTART_CINSALTAR; curr_padre = uint(v >> TSN_SBL_BIT_START_NUM_C); // ahora obtenemos su padre.. //--- if(curr_padre == idata) // dado que cur_padre apunta a la psocion 2.. [0][1 aqui] entonces con 1... return true; //--- curr_count = (uint)m_cinta[curr_padre]; // le devolvemos.. curr_state = TSNTRANSLAN_MAKESTATE(m_cinta[curr_padre - 1] & TSN_SBL_PARSE_MASK_OA_T); m_pos++; //--- TSNTRANSLATE_NEXT_NO_NEXT_KEY break; } case TSN_JSONTRANSLATE_TOK_COR_END: { //--- cacheamos el valor de cinta [curr_padre-1] = apunta a los datos const uint idata = --curr_padre; const ulong v = m_cinta[idata]; // ahora si rehacemos el valor con todo ya listo.. (curr padre apunta a la posicion correcta) m_cinta[idata] = TSN_JSONTRANSLATE_VTYPE_ARR | ulong(curr_count) << TSNTRANSLAN_BITSTART_NUMEL | ulong(m_cinta_pos - ((v >> 1) & TSN_SBL_PARSE_MASK_CINTAPOS_S)) << TSNTRANSLAN_BITSTART_CINSALTAR; curr_padre = uint(v >> TSN_SBL_BIT_START_NUM_C); // ahora obtenemos su padre.. //--- if(curr_padre == idata) // dado que cur_padre apunta a la psocion 2.. [0][1 aqui] entonces con 1... return true; //--- curr_count = (uint)m_cinta[curr_padre]; // le devolvemos.. curr_state = TSNTRANSLAN_MAKESTATE(m_cinta[curr_padre - 1] & TSN_SBL_PARSE_MASK_OA_T); m_pos++; //--- TSNTRANSLATE_NEXT_NO_NEXT_KEY break; } //--- Comment case TSN_JSONTRANSLATE_TOK_COMMENT_MQL: { //--- m_pos++; // salta el iniciador //--- Pase inicial 8 en 8 buscando el \n while(true) { // PrintFormat("Analizando: '%s'", CharArrayToString(m_raw, m_pos, 8)); const ulong chunk = (ulong)m_raw[m_pos] | (ulong)m_raw[m_pos + 1] << 8 | (ulong)m_raw[m_pos + 2] << 16 | (ulong)m_raw[m_pos + 3] << 24 | (ulong)m_raw[m_pos + 4] << 32 | (ulong)m_raw[m_pos + 5] << 40 | (ulong)m_raw[m_pos + 6] << 48 | (ulong)m_raw[m_pos + 7] << 56; //--- const ulong x = chunk ^ TSNTABLES_SWAR_MASK_NEW_LINE; const ulong has_x = TSNTABLES_SWAR_HAS(x); if(!has_x) { m_pos += 8; continue; } //--- const ulong flag_real = TSNTABLES_BIT_DEJAR_SOLO_MINUS_SIG_BYTE(has_x); m_pos += TSNTABLES_CTZ_64_GET_BYTE(flag_real); //--- skip ws while((_last_c = m_raw[++m_pos]) < 33) {} //--- verificamos if(TSN_JSONTRANSLATE_IS_COMMENT(_last_c)) continue; break; // Salimos ya con el caracter hecho } //--- m_next = m_next_p; // sigue break; } //--- Extensiones case TSN_JSONTRANSLATE_TOK_EXT_NONEXT_KEY: // solo se callea en comentarios.. { TSNTRANSLATE_NEXT_NO_NEXT_KEY break; } case TSN_JSONTRANSLATE_TOK_EXT_NEXT_OBJV: { TSNTRANSLATE_NEXT_OBJ_INITIAL(TSN_JSONTRANSLATE_TOK_EXT_NEXT_OBJV) break; } case TSN_JSONTRANSLATE_TOK_EXT_NEXT_VV: // Se callea en comentariso { TSNTRANSLATE_NEXT_VALUE(TSN_JSONTRANSLATE_TOK_EXT_NEXT_VV) break; } //--- invalid default: return false; } } } //--- } /* Templates: 1. Se itera por todo el texto se crea documento inicial 2. Ahora si en una segunda pasada se itera ahora sobre los "Strucutales" con un cursor sobre el tape.. La idea es armar el buf sin nada mas... { "valor" : " como estas amigo ? yo estoy bien " } */ //+------------------------------------------------------------------+ #endif // TSNTRANSLATIONS_SRC_LANBACKEND_PARSER_MQH