| //+------------------------------------------------------------------+
|
| //| 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
|