//+------------------------------------------------------------------+ //| JsonParser.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_JSONPARSER_MQH #define JSONPARSERBYLEO_SRC_JSONPARSER_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "JsonAsmDef.mqh" #include //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ struct CJsonNode; //#define JSONPARSERBYLEO_DESACTIVE_SWAR //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CJsonParser : public CBaseStructuredLan { private: //--- int m_stak_num[512]; int m_stak_pos[512]; char m_stak_state[512]; int m_stak_size; int m_stak_curr; int m_stak_curr_state; //--- ENUM_TSN_JSON_LAST_ERR m_last_err; //--- int CalcRegType(const string& file_name) override final; public: CJsonParser(void) {} ~CJsonParser(void) {} //--- // Para data raw se puede usar el json uchar directamtne bool AutoParseByFileExt(const int file_ext_type); __forceinline void CorrectPadding() { CorrectPadingInternal(' ');} //--- bool Parse(); bool ParseAssembly(); int ToJsonAsm(uchar& text[]) const; //--- __forceinline ENUM_TSN_JSON_LAST_ERR LastErr() const { return m_last_err; } //--- string Unescape(const int start, const int end) const; int Unescape(const int start, const int end, uchar& res[]) const; //--- __forceinline int GetStep(const int idx) const; __forceinline int GetStepPure(const int idx) const; //--- void PrintCintaTypes(const int start, const int num) const; //--- Comparacion de strings bool StrEquals(const string &str, const int curr) const; bool StrEquals(const int other, const int curr) const; //-- string CleanNode(int i, const int end) const; int CleanNode(int i, const int end, uchar &out[], int k, bool ajustar_arr) const; //--- CJsonNode GetRoot(); //--- bool NodeHashing(int curr, const int obj_count, const int bend, int& table_pos); //--- void ErrorInfo(bool with_avanze = false) const; //--- void SerializeToDom(CDomNodeBase *&root, CDomNodeManager *&manager, int cur, const int end) const; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CJsonParser::NodeHashing(int curr, const int obj_count, const int bend, int& table_pos) { //--- table_pos = FindNodePosOrCreate(curr); // Reservamos o obtenemos la posicion de este nodo //--- m_tables[table_pos].table_size = obj_count; // Numero de objetos m_tables[table_pos].start = curr; m_pfh.MaxValSeed(m_max_att++); m_pfh.InitAlg(obj_count, 0.75, fmin(obj_count, 4)); //--- ArrayResize(m_tables[table_pos].pos_arr, obj_count); //--- Obtnemos las posiciones de incio ulong keys_h[]; int posiciones[]; ArrayResize(keys_h, obj_count); ArrayResize(posiciones, obj_count); int pos_c = 0; curr += 2; // Ahora en key //--- while(curr < bend) { if(int(m_cinta[curr] & 0xF) != JSON_VTYPE_KEY) { curr += GetStep(curr); continue; } //--- m_tables[table_pos].pos_arr[pos_c] = curr; // Assing //--- ulong key_hash = FNV_OFFSET_BASIS; int i = int(m_cinta[curr] >> TSN_SBL_BIT_STR_START); // len const int len = i + int((m_cinta[curr] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN); // len //--- Hash 64 for(; i < len; i++) { key_hash ^= m_raw[i]; key_hash *= FNV_PRIME; } // PrintFormat("Hash para %s = %I64u", CharArrayToString(m_raw, int(m_cinta[curr] >> TSN_SBL_BIT_STR_START), // int((m_cinta[curr] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN)), key_hash); //--- keys_h[pos_c] = key_hash; posiciones[pos_c] = pos_c; pos_c++; //--- curr++; // salta KEY curr += GetStep(curr); // salta VAL } m_tables[table_pos].bucket_size = m_pfh.m_buckets_size; const int fn = m_pfh.m_final_table_size; m_tables[table_pos].final_table_size = fn; // Iniciamos.. ArrayResize(m_tables[table_pos].pos_index, fn); ArrayResize(m_tables[table_pos].key_hash, fn); ArrayInitialize(m_tables[table_pos].pos_index, -1); ArrayInitialize(m_tables[table_pos].key_hash, 0ULL); //--- return m_pfh.RunWValue(keys_h, posiciones, m_tables[table_pos].seeds, m_tables[table_pos].pos_index, m_tables[table_pos].key_hash); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CJsonParser::CalcRegType(const string& file_name) { return (int)JPBL_HashExt(file_name); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CJsonParser::StrEquals(const string &str, const int curr) const { //--- // m_cinta[m_cinta_pos++] = long(m_pos - 1) | long(start) << TSN_JSON_BIT_STR_START_END; const int start = int(m_cinta[curr] >> TSN_SBL_BIT_STR_START); const int len = int((m_cinta[curr] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN); //PrintFormat("Exact = '%s', other = '%s'", CharArrayToString(m_raw, start, len), str); const int l = StringLen(str); //--- if(l != len) return false; for(int i = 0; i < l; i++) { if(str[i] != m_raw[start + i]) return false; } return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CJsonParser::StrEquals(const int other, const int curr) const { //--- // m_cinta[m_cinta_pos++] = long(m_pos - 1) | long(start) << TSN_JSON_BIT_STR_START_END; const int start = int(m_cinta[curr] >> TSN_SBL_BIT_STR_START); const int len = int((m_cinta[curr] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN); //PrintFormat("Exact = '%s', other = '%s'", CharArrayToString(m_raw, start, len), str); const int s = int(m_cinta[other] >> TSN_SBL_BIT_STR_START); const int l = int((m_cinta[other] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN); //--- if(l != len) return false; for(int i = 0; i < l; i++) { if(m_raw[s + i] != m_raw[start + i]) return false; } return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ string CJsonParser::CleanNode(int i, const int end) const { //--- string str; int k = 0; StringSetLength(str, (end - i) + 1); // "\"\"" //--- bool in = false; for(i; i <= end; i++) { const uchar c = m_raw[i]; //--- if(in) { str.SetChar(k++, c); if(c == '\\') { str.SetChar(k++, m_raw[++i]); continue; } in = (c != '"'); } else { if(c < 33) continue; str.SetChar(k++, c); in = (c == '"'); } } //-- StringSetLength(str, k); return str; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CJsonParser::CleanNode(int i, const int end, uchar &out[], int k, bool ajustar_arr) const { //--- const int needed = (end - i) + 1; const int rsize = ArraySize(out); const int e = needed + k; if(rsize <= e) { ArrayResize(out, rsize + e); } //--- bool in = false; for(i; i <= end; i++) { const uchar c = m_raw[i]; //--- if(in) { out[k++] = c; if(c == '\\') { out[k++] = m_raw[++i]; continue; } in = (c != '"'); } else { if(c < 33) continue; out[k++] = c; in = (c == '"'); } } //-- if(ajustar_arr) // Ajustamos exacto ArrayResize(out, k); //-- return k - (e - needed); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CJsonParser::AutoParseByFileExt(const int file_ext_type) { switch(file_ext_type) { case TSN_SLF_RAW_FILE: { CorrectPadding(); // Auto return Parse(); } case TSN_SLF_RAW_EMBEBED: { return m_cinta_pos > 0 && m_len > 0; } case TSNJSON_RI_JSONASM_FILE: { CorrectPadding(); // Auto return ParseAssembly(); } default: { m_last_err = TSN_JSON_ERR_INVALID_EXT_FILE; m_pos = 0; return false; } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ //--- Especifico (C=caracter que se busca, T=Token asignado) #define TSN_JSON_NEXT_ESPECIFIC_DO(C, T) \ while(true) \ { \ if(m_raw[m_pos] == C) \ { \ m_next=T; \ break; \ } \ m_pos++;\ } //--- Generico // 1 en la tabl to token es INVALID (dara error..de malformed..) #define TSN_JSON_NEXT_NO_NEXT_KEY \ while(true) \ { \ const uchar _c = m_raw[m_pos]; \ if(_c < 33) \ { \ m_pos++; \ if(m_pos>=m_len) \ { \ m_last_err=TSN_JSON_ERR_MALFOMED_LEN_SUPERADO; \ return false; \ } \ continue; \ } \ const uchar _locked = m_next; \ m_next = g_table_json_tokens[_c];\ if(((1 << m_next) & g_json_expected[m_stak_curr_state][_locked]) == 0)\ { \ m_last_err= TSN_JSON_ERR_MALFORDMED_JSON; \ return false; \ }\ break; \ } //--- #define TSN_JSON_NEXT_LLAVE_INI \ while(true) \ { \ 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++;\ } //--- Luego de una coma en array // Luego de una , y KEY siempre siempre valor #define TSN_JSON_NEXT_VALUE_COMM_KEY \ while(true) \ { \ 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; \ return false; \ }\ break; \ } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CJsonParser::Parse() { //--- m_stak_size = 0; //m_stak_curr_state = -1; //m_stak_curr = -1; m_last_err = TSN_JSON_NOT_ERR; //m_next = TSN_JSON_TOK_INVALID; m_pos = m_offset; m_cinta_pos = 0; m_tables_size = 0; //--- 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: { //--- long v = 0; //--- Validamos signo long mask = 0; uchar ch = m_raw[m_pos]; if(ch == '-') { mask = -1; m_pos++; // Ahora en numero ch = m_raw[m_pos]; } //--- ch ^= '0'; //--- while(true) { //--- v = (v << 3) + (v << 1) + ch; //--- * m_pos++; //--- ch = m_raw[m_pos] ^ '0'; //--- if(ch < 10) { continue; } else if(ch == 30 || (ch | 0x20) == 117) // . { //--- double val = double(v); if(ch == 30) { m_pos++; long frac_int = 0; int fract_digits = 0; //--- while(true) { const uchar c = m_raw[m_pos] ^ '0'; if(c > 9) break; frac_int = (frac_int << 3) + (frac_int << 1) + c; fract_digits++; m_pos++; } //--- val += frac_int * g_Exp10[fract_digits]; } //--- 10e50.02500140 if((m_raw[m_pos] | 0x20) == 'e') // e or E { //--- m_pos++; bool exp_sign = true; uchar _c = m_raw[m_pos]; if(_c == '-') { exp_sign = false; m_pos++; } else if(_c == '+') { m_pos++; } //--- _c = m_raw[m_pos] ^ '0'; int exp_val = 0; //--- while(true) { exp_val = (exp_val << 3) + (exp_val << 1) + _c; //--- m_pos++; _c = m_raw[m_pos] ^ '0'; if(_c > 9) break; } //--- val *= (exp_sign) ? g_Pow10[exp_val] : g_Exp10[exp_val]; } //--- static BitInterpreter bit; bit.double_value = val; bit.long_value ^= (mask & (1LL << 63)); m_cinta[m_cinta_pos++] = JSON_VTYPE_REAL; m_cinta[m_cinta_pos++] = bit.long_value; break; } else { m_cinta[m_cinta_pos++] = JSON_VTYPE_INTEGER; m_cinta[m_cinta_pos++] = (v ^ mask) - mask; break; // Fin } } //--- no hago el m_pos++ dado que ahora mismo estamo en el seg char TSN_JSON_NEXT_NO_NEXT_KEY break; } //--- case TSN_JSON_TOK_STRING: { //--- //TSN_JSON_RESERVAR(2) //--- const int start = ++m_pos; //--- #ifndef JSONPARSERBYLEO_DESACTIVE_SWAR //--- 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; ulong has_bs = TSNTABLES_SWAR_HAS(x_bs); const ulong x_qt = chunk ^ TSNTABLES_SWAR_MASK_COMILLA; const ulong has_qt = TSNTABLES_SWAR_HAS(x_qt); //--- if((has_bs | has_qt) == 0) // Caso comun. .. ninguno de los dos.. continuamos.. { m_pos += 8; if((m_pos + 7) >= m_len) // Ultimas 8 posicinoes son " " asi que seria invalido ya.. { m_last_err = TSN_JSON_ERR_STRING_NOT_CLOSED; return false; } continue; } //--- if(has_bs == 0) // Si not iene bs (pero confirmamos que no es 0) // Entonces no tirne bs pero si qt salimos de una.. { const ulong flag_real = TSNTABLES_SWAR_64_DEJAR_SOLO_MINUS_SIG_BYTE(has_qt); //--- m_pos += TSNTABLES_SWAR_64_GET_BYTE(flag_real); m_cinta[m_cinta_pos++] = JSON_VTYPE_STRING | long(m_pos - start) << TSN_SBL_BIT_STR_LEN | long(start) << TSN_SBL_BIT_STR_START; m_pos++; // Luego del " break; } //--- Hay bs si o si pero tambien puede haber " const uchar compacted_bs = TSNTABLES_COMPACT_UL(has_bs); // Aislamos los bits donde empeizan las rachas de \ const uchar starts = compacted_bs & ~(compacted_bs << 1); // sumas especiifca para impar const ushort sum_impar_wide = (ushort)compacted_bs + ushort(starts & TSNTABLES_MASK_IMPARES); // Obtenemos finales de ambos... const uchar end_par = (compacted_bs + (starts & TSNTABLES_MASK_PARES)) & ~compacted_bs; const uchar end_impar = uchar(sum_impar_wide & 0xFF) & ~compacted_bs; // Ahora ya sabemos donde esta los finaes impares.. const uchar end_final = (end_impar & TSNTABLES_MASK_PARES) | (end_par & TSNTABLES_MASK_IMPARES); const uchar compacted_qt = TSNTABLES_COMPACT_UL(has_qt); const uchar qt_final = compacted_qt & ~end_final; //Print("nada"); //--- if(qt_final != 0) // Hay un qt real de cierre { // Avanzamos y cerramos.. m_pos += g_table_i8[qt_final]; m_cinta[m_cinta_pos++] = JSON_VTYPE_STRING | long(m_pos - start) << TSN_SBL_BIT_STR_LEN | long(start) << TSN_SBL_BIT_STR_START; m_pos++; // Luego del " break; } else { m_pos += 8; // Print(CharToString(m_raw[m_pos])); if((sum_impar_wide & 0x100) != 0) // Overflow el ultimo char es \ y el siguiente escapa { //Print("siuu"); m_pos++; // Uno mas.. luego del currend } // No hay ... o es escapado en caso exista if((m_pos + 7) >= m_len) // Ultimas 8 posicinoes son " " asi que seria invalido ya.. { m_last_err = TSN_JSON_ERR_STRING_NOT_CLOSED; return false; } } } #else // JSONPARSERBYLEO_DESACTIVE_SWAR //--- Char por char do { const uchar c = m_raw[m_pos]; //--- if(c == '\\') { m_pos += 2; continue; } if(c == '"') { // STRING 1 slot: [tipo(4)|len(28low)|start(32high)] m_cinta[m_cinta_pos++] = JSON_VTYPE_STRING | long(m_pos - start) << TSN_SBL_BIT_STR_LEN | long(start) << TSN_SBL_BIT_STR_START; // curr_end = INT_MAX; m_pos++; break; } //--- m_pos++; } while(m_pos < m_len); #endif // JSONPARSERBYLEO_DESACTIVE_SWAR //--- TSN_JSON_NEXT_NO_NEXT_KEY //--- break; } case TSN_JSON_TOK_BOOL_TRUE: { //TSN_JSON_RESERVAR(1) // t r u e W // [-4] [-3] [-2] [-1] [0] m_pos += 4; // 5 | 1 << TSN_SBL_BIT_START_BOOL(4) = 21 m_cinta[m_cinta_pos++] = 21; //--- TSN_JSON_NEXT_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] m_pos += 5; m_cinta[m_cinta_pos++] = JSON_VTYPE_BOOLEAN; //--- TSN_JSON_NEXT_NO_NEXT_KEY break; } case TSN_JSON_TOK_NULL: { //TSN_JSON_RESERVAR(1) // n u l l W // [-4][-3][-2][-1][] //--- m_pos += 4; m_cinta[m_cinta_pos++] = JSON_VTYPE_NULL; //--- TSN_JSON_NEXT_NO_NEXT_KEY break; } case TSN_JSON_TOK_COMMA: { //--- m_stak_num[m_stak_curr]++; //--- m_pos++; //--- if(m_stak_curr_state == TSN_JSON_CTX_IN_OBJ) { TSN_JSON_NEXT_ESPECIFIC_DO('"', TSN_JSON_TOK_KEY) } else { TSN_JSON_NEXT_VALUE_COMM_KEY } break; } case TSN_JSON_TOK_LLAVE_INI: { //TSN_JSON_RESERVAR(1) m_cinta[m_cinta_pos] = JSON_VTYPE_OBJ; //--- m_stak_curr = m_stak_size++; m_stak_curr_state = TSN_JSON_CTX_IN_OBJ; m_stak_state[m_stak_curr] = TSN_JSON_CTX_IN_OBJ; m_stak_num[m_stak_curr] = 0; m_stak_pos[m_stak_curr] = m_cinta_pos++; //--- m_cinta[m_cinta_pos++] = m_pos++; //--- TSN_JSON_NEXT_LLAVE_INI //--- break; } case TSN_JSON_TOK_COR_INI: { //TSN_JSON_RESERVAR(1) m_cinta[m_cinta_pos] = JSON_VTYPE_ARR; //--- m_stak_curr = m_stak_size++; m_stak_curr_state = TSN_JSON_CTX_IN_ARR; m_stak_state[m_stak_curr] = TSN_JSON_CTX_IN_ARR; m_stak_num[m_stak_curr] = 0; m_stak_pos[m_stak_curr] = m_cinta_pos++; //--- m_cinta[m_cinta_pos++] = m_pos++; //--- TSN_JSON_NEXT_NO_NEXT_KEY //--- break; } case TSN_JSON_TOK_LLAVE_END: case TSN_JSON_TOK_COR_END: { //--- if(m_cinta_pos > m_stak_pos[m_stak_curr] + 2) // [a][a][v][c] { m_stak_num[m_stak_curr]++; } //--- const int lp = m_stak_pos[m_stak_curr]; m_cinta[lp] |= long(m_stak_num[m_stak_curr]) << TSN_SBL_BIT_START_NUM_EL | long(m_cinta_pos - lp) << TSN_SBL_BIT_START_NUM_C; m_cinta[lp + 1] |= long(m_pos) << TSN_JSON_BIT_END_T; //--- m_stak_size = m_stak_curr--; //--- if(m_stak_size == 0) { return true; } //--- m_stak_curr_state = m_stak_state[m_stak_curr]; //--- m_pos++; //--- TSN_JSON_NEXT_NO_NEXT_KEY //--- break; } case TSN_JSON_TOK_KEY: { const int start = ++m_pos; //--- #ifndef JSONPARSERBYLEO_ACTIVE_SWAR_KEYS //--- Pase inicial 8 en 8 while(true) { // "asdasdasd".........." // 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; //--- #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS const ulong x_bs = chunk ^ TSNTABLES_SWAR_MASK_BACK_SLASH; ulong has_bs = TSNTABLES_SWAR_HAS(x_bs); #endif // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS const ulong x_qt = chunk ^ TSNTABLES_SWAR_MASK_COMILLA; const ulong has_qt = TSNTABLES_SWAR_HAS(x_qt); //--- #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS if((has_bs | has_qt) == 0) // Caso comun. .. ninguno de los dos.. continuamos.. { m_pos += 8; if((m_pos + 7) >= m_len) // Ultimas 8 posicinoes son " " asi que seria invalido ya.. { m_last_err = TSN_JSON_ERR_KEY_NOT_CLOSED; return false; } continue; } #else // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS if(has_qt == 0) // Caso comun. .. ninguno de los dos.. continuamos.. { m_pos += 8; if((m_pos + 7) >= m_len) // Ultimas 8 posicinoes son " " asi que seria invalido ya.. { m_last_err = TSN_JSON_ERR_KEY_NOT_CLOSED; return false; } continue; } #endif //--- #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS if(has_bs == 0) // Si not iene bs (pero confirmamos que no es 0) // Entonces no tirne bs pero si qt salimos de una.. { #endif // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS const ulong flag_real = TSNTABLES_SWAR_64_DEJAR_SOLO_MINUS_SIG_BYTE(has_qt); //--- m_pos += TSNTABLES_SWAR_64_GET_BYTE(flag_real); m_cinta[m_cinta_pos++] = JSON_VTYPE_KEY | long(m_pos - start) << TSN_SBL_BIT_STR_LEN | long(start) << TSN_SBL_BIT_STR_START; m_pos++; // Luego del " break; #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS } //--- Hay bs si o si pero tambien puede haber " const uchar compacted_bs = TSNTABLES_COMPACT_UL(has_bs); // Aislamos los bits donde empeizan las rachas de \ const uchar starts = compacted_bs & ~(compacted_bs << 1); // sumas especiifca para impar const ushort sum_impar_wide = (ushort)compacted_bs + ushort(starts & TSNTABLES_MASK_IMPARES); // Obtenemos finales de ambos... const uchar end_par = (compacted_bs + (starts & TSNTABLES_MASK_PARES)) & ~compacted_bs; const uchar end_impar = uchar(sum_impar_wide & 0xFF) & ~compacted_bs; // Ahora ya sabemos donde esta los finaes impares.. const uchar end_final = (end_impar & TSNTABLES_MASK_PARES) | (end_par & TSNTABLES_MASK_IMPARES); const uchar compacted_qt = TSNTABLES_COMPACT_UL(has_qt); const uchar qt_final = compacted_qt & ~end_final; //Print("nada"); //--- if(qt_final != 0) // Hay un qt real de cierre { // Avanzamos y cerramos.. m_pos += g_table_i8[qt_final]; m_cinta[m_cinta_pos++] = JSON_VTYPE_STRING | long(m_pos - start) << TSN_SBL_BIT_STR_LEN | long(start) << TSN_SBL_BIT_STR_START; m_pos++; // Luego del " break; } else { m_pos += 8; // Print(CharToString(m_raw[m_pos])); if((sum_impar_wide & 0x100) != 0) // Overflow el ultimo char es \ y el siguiente escapa { //Print("siuu"); m_pos++; // Uno mas.. luego del currend } // No hay ... o es escapado en caso exista if((m_pos + 7) >= m_len) // Ultimas 8 posicinoes son " " asi que seria invalido ya.. { m_last_err = TSN_JSON_ERR_KEY_NOT_CLOSED; return false; } } #endif // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS } #else // JSONPARSERBYLEO_ACTIVE_SWAR_KEYS //--- Char por char while(true) { #ifdef JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS const uchar c = m_raw[m_pos]; if(c == '\\') { m_pos += 2; continue; } if(c == '"') { m_cinta[m_cinta_pos++] = JSON_VTYPE_KEY | long(m_pos - start) << TSN_SBL_BIT_STR_LEN | long(start) << TSN_SBL_BIT_STR_START; // curr_end = INT_MAX; m_pos++; break; } #else // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS if(m_raw[m_pos] == '"') { m_cinta[m_cinta_pos++] = JSON_VTYPE_KEY | long(m_pos - start) << TSN_SBL_BIT_STR_LEN | long(start) << TSN_SBL_BIT_STR_START; // curr_end = INT_MAX; m_pos++; break; } #endif // JSONPARSERBYLEO_ENABLE_ESCAPED_COMMILAS_IN_KEYS //--- m_pos++; if(m_pos >= m_len) { m_last_err = TSN_JSON_ERR_KEY_NOT_CLOSED; return false; } } #endif // JSONPARSERBYLEO_ACTIVE_SWAR_KEYS //--- Find : while(true) { //--- if(m_raw[m_pos] == ':') break; //--- m_pos++; if(m_pos >= m_len) { m_last_err = TSN_JSON_ERR_MALFODERD_KEY_EXPECTED_DOS_PUNTOS; return false; } } //--- m_pos++; // Luego del //--- Buscamos valor TSN_JSON_NEXT_VALUE_COMM_KEY //--- break; } default: { m_last_err = TSN_JSON_ERR_INVALID_CHAR; return false; } } } //--- m_last_err = TSN_JSON_ERR_JSON_NOT_FINISH; //--- return false; } // "llabe":"llabe"; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CJsonParser::Unescape(const int start, const int end, uchar & res[]) const { //--- ArrayResize(res, (end - start) + 1); int pos = 0; //--- for(int i = start; i <= end; i++) { //--- if(m_raw[i] == '\\') { i++; const uchar c = m_raw[i]; const uchar next = g_tsn_table_unescape_table[c]; switch(next) { case TSN_TABLE_UNESCAPE_UNICODE: { if(i + 4 <= end) { i++; uint code = g_arr_valid_hex_chars[m_raw[i++]]; code = (code << 4) | g_arr_valid_hex_chars[m_raw[i++]]; code = (code << 4) | g_arr_valid_hex_chars[m_raw[i++]]; code = (code << 4) | g_arr_valid_hex_chars[m_raw[i]]; //--- Vericiacino para emojis.... if(code >= 0xD800 && code <= 0xDBFF) { // Otro mas \uxxxx if(i + 6 <= end && m_raw[i + 1] == '\\' && m_raw[i + 2] == 'u') { const int j = i + 3; uint low = g_arr_valid_hex_chars[m_raw[j]]; low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 1]]; low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 2]]; low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 3]]; //--- if(low >= 0xDC00 && low <= 0xDFFF) { code = 0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00); i = j + 3; // consumimos tambien el low surrogate } else { code = 0xFFFD; // low surrogate invalido } } else { code = 0xFFFD; // huerfano, no hay low surrogate despues } } //--- Low surrogate suelto (sin high antes) -> invalido else if(code >= 0xDC00 && code <= 0xDFFF) { code = 0xFFFD; } //--- if(code < 0x80) { res[pos++] = uchar(code); } else if(code < 0x800) { res[pos++] = uchar(0xC0 | (code >> 6)); res[pos++] = uchar(0x80 | (code & 0x3F)); } else if(code < 0x10000) { res[pos++] = uchar(0xE0 | (code >> 12)); res[pos++] = uchar(0x80 | ((code >> 6) & 0x3F)); res[pos++] = uchar(0x80 | (code & 0x3F)); } else { res[pos++] = uchar(0xF0 | (code >> 18)); res[pos++] = uchar(0x80 | ((code >> 12) & 0x3F)); res[pos++] = uchar(0x80 | ((code >> 6) & 0x3F)); res[pos++] = uchar(0x80 | (code & 0x3F)); } } else res[pos++] = 'u'; break; } case TSN_TABLE_UNESCAPE_HEX: { if(i + 2 <= end) { i++; uchar v = g_arr_valid_hex_chars[m_raw[i++]]; v = (v << 4) | g_arr_valid_hex_chars[m_raw[i]]; //Print("Valor:" , v); res[pos++] = v; } else res[pos++] = 'x'; break; } case TSN_TABLE_UNESCAPE_INVALID: { res[pos++] = c; break; } default: res[pos++] = next; break; } //--- continue; } //--- res[pos++] = m_raw[i]; } //--- return ArrayResize(res, pos); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ string CJsonParser::Unescape(const int start, const int end) const { //--- string res = ""; StringSetLength(res, (end - start) + 1); int pos = 0; //--- for(int i = start; i <= end; i++) { //--- if(m_raw[i] == '\\') { i++; const uchar c = m_raw[i]; const uchar next = g_tsn_table_unescape_table[c]; switch(next) { case TSN_TABLE_UNESCAPE_UNICODE: { if(i + 4 <= end) { i++; uint code = g_arr_valid_hex_chars[m_raw[i++]]; code = (code << 4) | g_arr_valid_hex_chars[m_raw[i++]]; code = (code << 4) | g_arr_valid_hex_chars[m_raw[i++]]; code = (code << 4) | g_arr_valid_hex_chars[m_raw[i]]; //--- High surrogate (0xD800-0xDBFF): el string es UTF-16, basta con // validar que venga acompañado de un low surrogate valido despues // y escribir ambas unidades tal cual (no hace falta combinar). if(code >= 0xD800 && code <= 0xDBFF) { if(i + 6 <= end && m_raw[i + 1] == '\\' && m_raw[i + 2] == 'u') { const int j = i + 3; uint low = g_arr_valid_hex_chars[m_raw[j]]; low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 1]]; low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 2]]; low = (low << 4) | g_arr_valid_hex_chars[m_raw[j + 3]]; //--- if(low >= 0xDC00 && low <= 0xDFFF) { res.SetChar(pos++, (ushort)code); res.SetChar(pos++, (ushort)low); i = j + 3; // consumimos tambien el low surrogate } else { res.SetChar(pos++, 0xFFFD); // low surrogate invalido } } else { res.SetChar(pos++, 0xFFFD); // huerfano, no hay low surrogate despues } } //--- Low surrogate suelto (sin high antes) -> invalido else if(code >= 0xDC00 && code <= 0xDFFF) { res.SetChar(pos++, 0xFFFD); } else { res.SetChar(pos++, (ushort)code); } } else res.SetChar(pos++, 'u'); break; } case TSN_TABLE_UNESCAPE_HEX: { if(i + 2 <= end) { i++; uchar v = g_arr_valid_hex_chars[m_raw[i++]]; v = (v << 4) | g_arr_valid_hex_chars[m_raw[i]]; //Print("Valor:" , v); res.SetChar(pos++, v); } else res.SetChar(pos++, 'x'); break; } case TSN_TABLE_UNESCAPE_INVALID: { res.SetChar(pos++, c); break; } default: res.SetChar(pos++, next); break; } //--- continue; } //--- res.SetChar(pos++, m_raw[i]); } //--- res.Truncate(pos); //--- return res; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ __forceinline int CJsonParser::GetStepPure(const int idx) const { const int tipo = int(m_cinta[idx] & 0xF); switch(tipo) { case JSON_VTYPE_KEY: return 1; case JSON_VTYPE_OBJ: case JSON_VTYPE_ARR: case JSON_VTYPE_INTEGER: case JSON_VTYPE_REAL: return 2; case JSON_VTYPE_BOOLEAN: case JSON_VTYPE_STRING: case JSON_VTYPE_NULL: return 1; } return 1; } //+------------------------------------------------------------------+ //| GetStep: posiciones que ocupa el nodo idx en la cinta | //+------------------------------------------------------------------+ __forceinline int CJsonParser::GetStep(const int idx) const { switch(int(m_cinta[idx] & 0xF)) { case JSON_VTYPE_KEY: return 1; case JSON_VTYPE_OBJ: case JSON_VTYPE_ARR: return int(m_cinta[idx] >> TSN_SBL_BIT_START_NUM_C); case JSON_VTYPE_INTEGER: case JSON_VTYPE_REAL: return 2; case JSON_VTYPE_BOOLEAN: case JSON_VTYPE_STRING: case JSON_VTYPE_NULL: return 1; } return 1; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CJsonNode CJsonParser::GetRoot() { if(m_cinta_pos == 0) return EMPTY_JSON_NODE; return CJsonNode(&this, 0, GetStep(0)); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CJsonParser::SerializeToDom(CDomNodeBase *&root, CDomNodeManager *&manager, int cur, const int end) const { //--- Temp CDomNodeBase* prev_value; // valolr qeu devuelve key CDomNodeBase* current_container[512]; // contenedor int current_ends[512]; int sp = 0; //--- manager = CDomNodeManager(); root = new CDomNodeBase(manager); //--- Inicial const int c = int((m_cinta[cur] >> TSN_SBL_BIT_START_NUM_EL) & TSN_SBL_BIT_MASK_NUM_EL); if((m_cinta[cur]&TSN_SBL_BIT_MASK_TYPE) == JSON_VTYPE_OBJ) root.NewObj(c); else root.NewArr(c); //--- current_container[0] = root; current_ends[sp] = end; //--- cur += 2; //--- It while(true) { //--- Check if(cur >= current_ends[sp]) { sp--; // Nivel menos if(sp <= 0) return; } //--- const ENUM_JSON_VTYPE tipo = ENUM_JSON_VTYPE(m_cinta[cur] & TSN_SBL_BIT_MASK_TYPE); //--- switch(tipo) { case JSON_VTYPE_KEY: { const int s = int(m_cinta[cur] >> TSN_SBL_BIT_STR_START); const int slen = int((m_cinta[cur] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN); const string str = Unescape(s, s + slen - 1); prev_value = root[str]; break; } case JSON_VTYPE_OBJ: { const int count = int((m_cinta[cur] >> TSN_SBL_BIT_START_NUM_EL) & TSN_SBL_BIT_MASK_NUM_EL); if(current_container[sp].m_type == JSON_VTYPE_ARR) // [{}] { const int lock = sp++; current_container[sp] = new CDomNodeBase(current_container[sp].m_ctx); current_container[sp].NewObj(count); current_container[lock].AddItem(current_container[sp]); } else // {key:{}} { sp++; prev_value = new CDomNodeBase(current_container[sp].m_ctx); prev_value.NewObj(count); current_container[sp] = prev_value; } current_ends[sp] = cur + int(m_cinta[cur] >> TSN_SBL_BIT_START_NUM_C); break; } case JSON_VTYPE_ARR: { const int count = int((m_cinta[cur] >> TSN_SBL_BIT_START_NUM_EL) & TSN_SBL_BIT_MASK_NUM_EL); if(current_container[sp].m_type == JSON_VTYPE_ARR) { const int lock = sp++; current_container[sp] = new CDomNodeBase(current_container[sp].m_ctx); current_container[sp].NewArr(count); current_container[lock].AddItem(current_container[sp]); } else // {key:[]} { sp++; prev_value = new CDomNodeBase(current_container[sp].m_ctx); prev_value.NewArr(count); current_container[sp] = prev_value; } current_ends[sp] = cur + int(m_cinta[cur] >> TSN_SBL_BIT_START_NUM_C); break; } case JSON_VTYPE_INTEGER: { if(current_container[sp].m_type == JSON_VTYPE_ARR) current_container[sp].AddItem(new CDomNodeBase(current_container[sp].m_ctx, JSON_VTYPE_INTEGER, m_cinta[cur + 1])); else prev_value = m_cinta[cur + 1]; break; } case JSON_VTYPE_REAL: { static BitInterpreter un; un.long_value = m_cinta[cur + 1]; //--- if(current_container[sp].m_type == JSON_VTYPE_ARR) current_container[sp].AddItem(new CDomNodeBase(current_container[sp].m_ctx, JSON_VTYPE_REAL, un.double_value)); else prev_value = un.double_value; break; } case JSON_VTYPE_BOOLEAN: { const bool v = ((m_cinta[cur] >> TSN_SBL_BIT_START_ENDTYPE) & 1) != 0; //--- if(current_container[sp].m_type == JSON_VTYPE_ARR) current_container[sp].AddItem(new CDomNodeBase(current_container[sp].m_ctx, JSON_VTYPE_BOOLEAN, v)); else prev_value = v; break; } case JSON_VTYPE_STRING: { const int s = int(m_cinta[cur] >> TSN_SBL_BIT_STR_START); const int slen = int((m_cinta[cur] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN); const string str = Unescape(s, s + slen - 1); //--- if(current_container[sp].m_type == JSON_VTYPE_ARR) current_container[sp].AddItem(new CDomNodeBase(current_container[sp].m_ctx, JSON_VTYPE_STRING, str)); else prev_value = str; break; } case JSON_VTYPE_NULL: { //--- if(current_container[sp].m_type == JSON_VTYPE_ARR) current_container[sp].AddItem(new CDomNodeBase(current_container[sp].m_ctx)); else prev_value.m_type = JSON_VTYPE_NULL; break; } default: break; } //--- cur += GetStepPure(cur); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CJsonParser::PrintCintaTypes(const int start, const int num) const { //--- int p = fmax(start, 0); const int end = (num == WHOLE_ARRAY || (p + num) > m_cinta_pos) ? m_cinta_pos : (p + num); //--- while(p < end) { const ENUM_JSON_VTYPE tipo = ENUM_JSON_VTYPE(m_cinta[p] & TSN_SBL_BIT_MASK_TYPE); string text = "[" + (string)p + "] " + EnumToString(tipo) + " | "; //--- switch(tipo) { case JSON_VTYPE_KEY: { const int s = int(m_cinta[p] >> TSN_SBL_BIT_STR_START); const int slen = int((m_cinta[p] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN); text += "start=" + (string)s + " len=" + (string)slen + " key=\"" + CharArrayToString(m_raw, s, slen) + "\""; break; } case JSON_VTYPE_OBJ: case JSON_VTYPE_ARR: { const int count = int((m_cinta[p] >> TSN_SBL_BIT_START_NUM_EL) & TSN_SBL_BIT_MASK_NUM_EL); const int num_c = int(m_cinta[p] >> TSN_SBL_BIT_START_NUM_C); const int raw_s = int(m_cinta[p + 1] & 0xFFFFFFFF); const int raw_e = int(m_cinta[p + 1] >> TSN_JSON_BIT_END_T); text += "count=" + (string)count + " num_c=" + (string)num_c + " raw_start=" + (string)raw_s + " raw_end=" + (string)raw_e; break; } case JSON_VTYPE_INTEGER: { text += "val=" + (string)m_cinta[p + 1]; break; } case JSON_VTYPE_REAL: { static BitInterpreter un; un.long_value = m_cinta[p + 1]; text += "val=" + (string)un.double_value; break; } case JSON_VTYPE_BOOLEAN: text += "val=" + (string)(bool)((m_cinta[p] >> TSN_SBL_BIT_START_ENDTYPE) & 1); break; case JSON_VTYPE_STRING: { const int s = int(m_cinta[p] >> TSN_SBL_BIT_STR_START); const int slen = int((m_cinta[p] >> TSN_SBL_BIT_STR_LEN) & TSN_SBL_BIT_STR_MASK_LEN); text += "start=" + (string)s + " len=" + (string)slen + " val=\"" + Unescape(s, s + slen - 1) + "\""; break; } case JSON_VTYPE_NULL: text += "null"; break; } Print(text); p += GetStepPure(p); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CJsonParser::ErrorInfo(bool with_avanze = false) const { if(m_last_err == TSN_JSON_NOT_ERR) return; //--- int col, line; GetLastErrorLocation(col, line); //--- string desc; switch(m_last_err) { case TSN_JSON_ERR_INVALID_CHAR_COMMA_IN_CTX_NO_OBJ_ARR: desc = "Coma invalida fuera de un objeto o arreglo"; break; case TSN_JSON_ERR_MALFORDMED_JSON: desc = "JSON malformado"; break; case TSN_JSON_ERR_INVALID_CHAR: desc = "Caracter invalido"; break; case TSN_JSON_ERR_MALFORMED_TRUE: desc = "Literal 'true' malformado"; break; case TSN_JSON_ERR_MALFORMED_FALSE: desc = "Literal 'false' malformado"; break; case TSN_JSON_ERR_MALFORMED_NULL: desc = "Literal 'null' malformado"; break; case TSN_JSON_ERR_STRING_NOT_CLOSED: desc = "Cadena sin cerrar"; break; case TSN_JSON_ERR_INVALID_STATE: desc = "Estado invalido del parser"; break; case TSN_JSON_ERR_JSON_NOT_FINISH: desc = "El JSON no termino correctamente"; break; case TSN_JSON_ERR_EXPECTED_OBJ_ARR: desc = "Se esperaba un objeto o un arreglo"; break; case TSN_JSON_ERR_OVERFLOW_IN_CINTA_RESIZE: desc = "Overflow al redimensionar la cinta interna"; break; case TSN_JSON_ERR_MALFODERD_KEY_EXPECTED_DOS_PUNTOS: desc = "Se esperaban dos puntos ':' despues de la clave"; break; case TSN_JSON_ERR_MALFOMED_LEN_SUPERADO: desc = "Longitud maxima superada"; break; case TSN_JSON_ERR_KEY_NOT_CLOSED: desc = "Clave sin cerrar"; break; case TSN_JSON_ERR_HAS_NOT_CONTENT: desc = "El JSON no tiene contenido"; break; case TSN_JSON_ERR_INVALID_EXT_FILE: desc = "Extension de archivo invalida"; break; default: desc = "Error desconocido"; break; } //--- PrintFormat("%s [Line %d, Col %d]", desc, line, col); if(with_avanze) { Print("Avanze: "); Print(CharArrayToString(m_raw, 0, m_pos)); } } } #endif // JSONPARSERBYLEO_SRC_JSONPARSER_MQH //+------------------------------------------------------------------+ /* // // a\\\"\nb B = 0b01110100 ----- ~B<<1 = 0b00010111 starts= 0b00010100 0b01110100 sum = 0b00010100 10001000 10001000 01110100 (and) 00000000 ----- B = "\\a\\bb B = 01101100 B<<1 = 11011000 ~B = 00100111 -- starts = 01101100 00100111 and 00100100 -- = 01101100 + 00100100 10010000 -- eo = 10010000 01101100 00000000 */ //+------------------------------------------------------------------+