JsonParserByLeo/Src/JsonAsm.mqh
Nique_372 90eb25f46a
2026-07-20 12:04:54 -05:00

869 lines
24 KiB
MQL5

//+------------------------------------------------------------------+
//| JsonAsm.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_JSONASM_MQH
#define JSONPARSERBYLEO_SRC_JSONASM_MQH
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#include "JsonParser.mqh"
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
namespace TSN
{
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int CJsonParser::ToJsonAsm(uchar& text[]) const
{
//---
int p = 0; // Lectura de cinta
int w = 0; // Escritura en text
//---
ArrayResize(text, (m_len << 1));
//--- Pila de posiciones de "muerte" (en slots de cinta) de cada ARR/OBJ abierto
int end_stack[512];
int stack_curr = -1;
//---
while(p < m_cinta_pos)
{
//---
const ENUM_JSON_VTYPE tipo = ENUM_JSON_VTYPE(m_cinta[p] & 0xF);
//---
switch(tipo)
{
//---
case JSON_VTYPE_KEY:
{
// KEY 1 slot: [tipo(4)|len(28low)|start(32high)] (igual que 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[w++] = 'K';
text[w++] = ' ';
text[w++] = '"';
w += ArrayCopy(text, m_raw, w, s, slen);
text[w++] = '"';
text[w++] = '\n';
break;
}
//---
case JSON_VTYPE_OBJ:
case JSON_VTYPE_ARR:
{
text[w++] = tipo == JSON_VTYPE_ARR ? 'A' : 'O';
text[w++] = ' ';
text[w++] = '\n';
//--- num_c = slots de cinta que ocupa este contenedor completo (incluyéndose a sí mismo)
const int num_c = int(m_cinta[p] >> TSN_SBL_BIT_START_NUM_C);
//--- Apilamos dónde "muere" este nivel, no cuántos elementos le faltan
stack_curr++;
end_stack[stack_curr] = p + num_c;
break;
}
case JSON_VTYPE_INTEGER:
{
text[w++] = 'R';
text[w++] = ' ';
long num = m_cinta[p + 1];
if(num == 0)
{
text[w++] = '0';
}
else
{
w += StringToCharArray(string(num), text, w, WHOLE_ARRAY, CP_UTF8) - 1;
}
//---
text[w++] = '\n';
text[w++] = '-';
text[w++] = '\n';
break;
}
//---
case JSON_VTYPE_REAL:
{
//---
static BitInterpreter bit;
bit.long_value = m_cinta[p + 1];
double num = bit.double_value;
//---
text[w++] = 'R';
text[w++] = ' ';
w += StringToCharArray(string(num), text, w, WHOLE_ARRAY, CP_UTF8) - 1;
//---
text[w++] = '\n';
text[w++] = '-';
text[w++] = '\n';
break;
}
//--
case JSON_VTYPE_BOOLEAN:
{
text[w++] = bool((m_cinta[p] >> TSN_SBL_BIT_START_ENDTYPE) & 1) ? 'T' : 'F';
text[w++] = '\n';
text[w++] = '-';
text[w++] = '\n';
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[w++] = 'S';
text[w++] = ' ';
text[w++] = '"';
w += ArrayCopy(text, m_raw, w, s, slen);
text[w++] = '"';
text[w++] = '\n';
text[w++] = '-';
text[w++] = '\n';
break;
}
//---
case JSON_VTYPE_NULL:
{
text[w++] = 'N';
text[w++] = '\n';
text[w++] = '-';
text[w++] = '\n';
break;
}
}
//---
p += GetStepPure(p);
//--- Cerramos en cascada todos los niveles cuya posición de muerte ya alcanzamos.
// Esto reemplaza tanto el manejo de "vacío" como la cascada de pops: ya no hace
// falta tocar nada dentro de cada case.
while(stack_curr >= 0 && p == end_stack[stack_curr])
{
text[w++] = 'P';
text[w++] = ' ';
text[w++] = '\n';
//---
stack_curr--;
//---
if(stack_curr >= 0)
{
text[w++] = '-';
text[w++] = ' ';
text[w++] = '\n';
}
}
}
//---
return ArrayResize(text, w);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CJsonParser::ParseAssembly(void)
{
//--- Variables init
m_cinta_pos = 0;
m_pos = m_offset;
m_last_err = TSN_JSON_NOT_ERR;
m_next = TSN_JSON_TOKEN_ASM_ITER;
m_stak_curr = -1;
//---
while(true)
{
switch(m_next)
{
//---
case JSON_VTYPE_KEY:
{
//---
m_pos += 2;
//---
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
//---
m_next = TSN_JSON_TOKEN_ASM_ITER;
break;
}
//---
case JSON_VTYPE_OBJ: // OBJ [COUNT] [STEP]
{
//---
m_cinta[m_cinta_pos] = JSON_VTYPE_OBJ;
//---
m_stak_curr++;
m_stak_num[m_stak_curr] = 0;
m_stak_pos[m_stak_curr] = m_cinta_pos++;
//---
m_cinta[m_cinta_pos++] = m_pos++;
//---
m_next = TSN_JSON_TOKEN_ASM_ITER;
//---
break;
}
//---
case JSON_VTYPE_ARR: // ARR [COUNT] [STEP]
{
//---
m_cinta[m_cinta_pos] = JSON_VTYPE_ARR;
//---
m_stak_curr++;
m_stak_num[m_stak_curr] = 0;
m_stak_pos[m_stak_curr] = m_cinta_pos++;
//---
m_cinta[m_cinta_pos++] = m_pos++;
//---
m_next = TSN_JSON_TOKEN_ASM_ITER;
//---
break;
}
//---
case JSON_VTYPE_INTEGER:
case JSON_VTYPE_REAL: // REAL [Valor]
{
//---
m_pos += 2;
//---
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
}
}
//---
m_next = TSN_JSON_TOKEN_ASM_ITER;
break;
}
//---
case JSON_VTYPE_BOOLEAN: // TRUE (solo...)
{
//---
m_pos++;
// 5 | 1 << TSN_SBL_BIT_START_BOOL(4) = 21
m_cinta[m_cinta_pos++] = 21;
//---
m_next = TSN_JSON_TOKEN_ASM_ITER;
break;
}
//---
case JSON_VTYPE_STRING:
{
//---
m_pos += 2; // S[][Aqui]
//---
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
//--- 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
//---
m_next = TSN_JSON_TOKEN_ASM_ITER;
break;
}
//---
case JSON_VTYPE_NULL:
{
m_cinta[m_cinta_pos++] = JSON_VTYPE_NULL;
m_pos++;
m_next = TSN_JSON_TOKEN_ASM_ITER;
break;
}
//---
case TSN_JSON_TOKEN_ASM_BOOL_FALSE: // FALSE [value=1\0]
{
//---
m_pos++;
m_cinta[m_cinta_pos++] = JSON_VTYPE_BOOLEAN;
//---
m_next = TSN_JSON_TOKEN_ASM_ITER;
break;
}
//---
case TSN_JSON_TOKEN_ASM_INVALID: //
{
//Print("Err: ", m_next, " Pos: ", m_raw[m_pos]);
m_last_err = TSN_JSON_ERR_MALFORDMED_JSON;
return false;
}
//---
case TSN_JSON_TOKEN_ASM_POP:
{
//---
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;
//---
//PrintFormat("Stack curr %d | Size = %d | Num %d | LP %d - Cinta pos %d", m_stak_curr, m_stak_size, m_stak_num[m_stak_curr], lp, m_cinta_pos);
//---
m_stak_curr--;
m_pos++; // Luego del P
//---
if(m_stak_curr < 0)
{
return true;
}
//---
m_next = TSN_JSON_TOKEN_ASM_ITER;
break;
}
//---
case TSN_JSON_TOKEN_ASM_ADD_EL:
{
m_stak_num[m_stak_curr]++;
m_pos++;
m_next = TSN_JSON_TOKEN_ASM_ITER;
break;
}
//---
case TSN_JSON_TOKEN_ASM_ITER:
{
// JUSTO luego del \n
//---
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;
}
//---
m_next = g_tsn_jsonasm_char_to_token[c];
break;
}
//---
break;
}
//---
case TSN_JSON_TOKEN_ASM_SALTAR_NL:
{
//---
#ifdef JSONPARSERBYLEO_ACTIVE_SWAR_IN_FIND_NL
//---
int curr_end = m_pos + 8;
while(curr_end <= m_len)
{
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 == 0)
{
m_pos = curr_end;
curr_end += 8;
continue;
}
//---
break;
}
#endif // JSONPARSERBYLEO_DESACTIVE_SWAR_IN_FIND_NL
do
{
if(m_raw[m_pos] == '\n')
break;
m_pos++;
}
while(m_pos < m_len);
// En caso supere el len haciendo esto
//Print("Char: ", m_raw[m_pos]);
m_pos++; // Luego del \n
//---
m_next = TSN_JSON_TOKEN_ASM_ITER;
break;
}
default:
{
// Print("Err: " , m_next);
m_last_err = TSN_JSON_ERR_MALFORDMED_JSON;
return false;
}
}
}
}
//---End
}
//+------------------------------------------------------------------+
#endif // JSONPARSERBYLEO_SRC_JSONASM_MQH