forked from nique_372/BasesParserSLan
369 lines
11 KiB
MQL5
369 lines
11 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| Main.mqh |
| |
//| Copyright 2026, Niquel Mendoza. |
| |
//| https://www.mql5.com |
| |
//+------------------------------------------------------------------+
| |
#property copyright "Copyright 2026, Niquel Mendoza."
| |
#property link "https://www.mql5.com"
| |
#property strict
| |
| |
#ifndef BASESPARSERSLAN_UE_MAIN_MQH
| |
#define BASESPARSERSLAN_UE_MAIN_MQH
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
#include <TSN\\Tables\\UE.mqh>
| |
#include "Def.mqh"
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
namespace TSN
| |
{
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
// Esta clase como tal se encargar de desescapar strings.. con reglas de escape
| |
// ya depende del elngauje final...
| |
// aparte el backend como tal debera de proporcional una tabla de escapes validos
| |
//--- optimizaciones
| |
// estuve busncado buen rato un par de horas... un buen algoritmo paara utf8 branchless
| |
// etc.. pero no tuve buenos reusltoas el tema es uqe un CLZ banrhacles etc para contar bytes requerido para utf8
| |
// en mql5 es mas costoso incluso con miss predict que una verisno de ternarios ifs...
| |
// y en parte tiene snrinfgo CLZ + otra talba son mas cosots, acceso a memoeira luego vaiors shifts or etc..
| |
// dado qeu eMQL5 no tieen clz nativo, hay que simualres con brujin y patrones de bits..
| |
// asi qeu creo que sincmante este algoetim de ifelseif parece ser el indicado para MQL5
| |
// no veo uan alternativa mejor para esto.. o que logre superarlo las version con CLZ o branhclees
| |
// son en promedio un 40-300% mas lentas, asi que me quedare con la via "simple y legible" en MQL6
| |
// no todo es brancless aqui .. se veia venir tmb
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
class CBSFLUnescape
| |
{
| |
public:
| |
CBSFLUnescape(void) {}
| |
~CBSFLUnescape(void) {}
| |
| |
//--- Basicos
| |
// string \ uarray
| |
// decodifica los ecapes a utf16
| |
template <typename TBackend>
| |
static bool MakeString(const uchar &in[], int i, int len, string& res);
| |
| |
| |
// decodifica los escapes a utf8
| |
template <typename TBackend, typename TUnicodeOne>
| |
static int MakeUArray(const uchar &in[], int i, int len, uchar &res[], int pos = 0);
| |
| |
| |
// decodifica los ecapes a un hash compatible con vm
| |
template <typename TBackend>
| |
static bool MakeHash(const uchar &in[], int& i, int len, ulong& hash);
| |
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
template <typename TBackend>
| |
static bool CBSFLUnescape::MakeHash(const uchar &in[], int& i, int len, ulong &hash)
| |
{
| |
//---
| |
hash = 14695981039346656037ULL;
| |
for(;; i++) // nota aqui el " se nos garantiza pero igual en uncidoe hay que vertiicar el len
| |
{
| |
//---
| |
const uchar ch = in[i];
| |
if(ch == '\\')
| |
{
| |
const uchar next = TBackend::s_unescape_table[in[++i]];
| |
switch(next)
| |
{
| |
case TSN_TABLE_UNESCAPE_U32:
| |
{
| |
//--- U32 si requiere "desescaparlo"
| |
if(i + 8 >= len)
| |
return false;
| |
DOMBASEULTIS_U_U32_MAKESTRING_CODE(code, i)
| |
if(code > 0x10FFFF)
| |
return false;
| |
| |
//---
| |
if(code <= 0xffff)
| |
{
| |
DOMBASEULTIS_U_MKH_ITER(code)
| |
}
| |
else // surrogates
| |
{
| |
code -= 0x10000;
| |
DOMBASEULTIS_U_MKH_ITER(ushort(0xD800 + (code >> 10)))
| |
DOMBASEULTIS_U_MKH_ITER(ushort(0xDC00 + (code & 0x3FF)))
| |
}
| |
break;
| |
}
| |
case TSN_TABLE_UNESCAPE_HEX:
| |
{
| |
if(i + 2 >= len)
| |
return false;
| |
| |
//---
| |
uchar v = g_arr_valid_hex_chars[in[++i]] << 4;
| |
v |= g_arr_valid_hex_chars[in[++i]];
| |
DOMBASEULTIS_U_MKH_ITER(v)
| |
break;
| |
}
| |
case TSN_TABLE_UNESCAPE_UNICODE:
| |
{
| |
if(i + 4 >= len)
| |
return false;
| |
| |
//---
| |
DOMBASEULTIS_U_UNICODE_MAKESTRING_CODE(code, i)
| |
DOMBASEULTIS_U_MKH_ITER(code)
| |
break;
| |
}
| |
case TSN_TABLE_UNESCAPE_INVALID:
| |
return false;
| |
default:
| |
DOMBASEULTIS_U_MKH_ITER(next)
| |
break;
| |
}
| |
continue;
| |
}
| |
if(ch == '"')
| |
{
| |
i++; // lo dejamos luego del "
| |
return true;
| |
}
| |
| |
//--
| |
DOMBASEULTIS_U_MKH_ITER(ch)
| |
}
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
// 1. se saume un input valido en utf8 etc...
| |
// 2. Ahora lo qeu hace esta funcion es desescapar y aparte los \uXXXX como mql5 es UTF16
| |
// los pone tal cual.. ahora los UTF32 (\UXXXXXXXX) auqi si hay logica de surrogate etc..
| |
template <typename TBackend>
| |
static bool CBSFLUnescape::MakeString(const uchar &in[], int i, int len, string& res)
| |
{
| |
//---
| |
StringInit(res, len, ' ');
| |
int pos = 0;
| |
| |
//---
| |
len += i;
| |
for(; i < len; i++)
| |
{
| |
//---
| |
const uchar ch = in[i];
| |
if(ch == '\\')
| |
{
| |
const uchar next = TBackend::s_unescape_table[in[++i]];
| |
switch(next)
| |
{
| |
case TSN_TABLE_UNESCAPE_U32:
| |
{
| |
//--- U32 si requiere "desescaparlo"
| |
if(i + 8 >= len)
| |
return false;
| |
DOMBASEULTIS_U_U32_MAKESTRING_CODE(code, i)
| |
if(code > 0x10FFFF)
| |
return false;
| |
| |
//---
| |
if(code <= 0xffff)
| |
{
| |
res.SetChar(pos++, (ushort)code);
| |
}
| |
else // surrogates
| |
{
| |
code -= 0x10000;
| |
res.SetChar(pos++, ushort(0xD800 + (code >> 10))); // high
| |
res.SetChar(pos++, ushort(0xDC00 + (code & 0x3FF))); // low
| |
}
| |
break;
| |
}
| |
case TSN_TABLE_UNESCAPE_HEX:
| |
{
| |
if(i + 2 >= len)
| |
return false;
| |
| |
//---
| |
uchar v = g_arr_valid_hex_chars[in[++i]] << 4;
| |
v |= g_arr_valid_hex_chars[in[++i]];
| |
res.SetChar(pos++, v);
| |
break;
| |
}
| |
case TSN_TABLE_UNESCAPE_UNICODE:
| |
{
| |
if(i + 4 >= len)
| |
return false;
| |
| |
//---
| |
DOMBASEULTIS_U_UNICODE_MAKESTRING_CODE(code, i)
| |
res.SetChar(pos++, code);
| |
break;
| |
}
| |
case TSN_TABLE_UNESCAPE_INVALID:
| |
return false;
| |
default:
| |
res.SetChar(pos++, next);
| |
break;
| |
}
| |
continue;
| |
}
| |
| |
//--
| |
res.SetChar(pos++, ch);
| |
}
| |
| |
//---
| |
res.Truncate(pos);
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
// Toma los bytes in y luego para los escapes \u \U \x los pasa utf8
| |
template <typename TBackend, typename TUnicodeOne>
| |
static int CBSFLUnescape::MakeUArray(const uchar &in[], int i, int len, uchar &res[], int pos = 0)
| |
{
| |
//---
| |
ArrayResize(res, len);
| |
| |
//---
| |
len += i;
| |
for(; i < len; i++)
| |
{
| |
//---
| |
const uchar ch = in[i];
| |
if(ch == '\\')
| |
{
| |
const uchar next = TBackend::s_unescape_table[in[++i]];
| |
switch(next)
| |
{
| |
case TSN_TABLE_UNESCAPE_U32:
| |
{
| |
if(i + 8 >= len)
| |
return false;
| |
| |
//---
| |
DOMBASEULTIS_U_U32_MAKESTRING_CODE(code, i)
| |
if(code > 0x10FFFF)
| |
return false;
| |
| |
//--- logica aqui...
| |
BSFL_WRITE_ASCCI(code)
| |
else
| |
if(code < 0x10000)
| |
{
| |
BSFL_UTF8_WRITE_3(code)
| |
}
| |
else
| |
{
| |
BSFL_UTF8_WRITE_4(code)
| |
}
| |
| |
//---
| |
break;
| |
}
| |
case TSN_TABLE_UNESCAPE_HEX:
| |
{
| |
if(i + 2 >= len)
| |
return false;
| |
| |
//---
| |
uchar v = g_arr_valid_hex_chars[in[++i]] << 4;
| |
v |= g_arr_valid_hex_chars[in[++i]];
| |
| |
//---
| |
if(v < 0x80)
| |
{
| |
res[pos++] = (v);
| |
}
| |
else
| |
{
| |
res[pos++] = (0xC0 | (v >> 6));
| |
res[pos++] = (0x80 | (v & 0x3F));
| |
}
| |
break;
| |
}
| |
case TSN_TABLE_UNESCAPE_UNICODE:
| |
{
| |
if(i + 4 >= len)
| |
return -1; // error
| |
| |
//---
| |
DOMBASEULTIS_U_UNICODE_MAKESTRING_CODE(code, i)
| |
| |
//---
| |
BSFL_WRITE_ASCCI(code)
| |
| |
//--- verificamos si el unicode one esta presente (osea si no veriicamos surrogates si no tal cual como yaml)
| |
// o json que si hay que verificar...
| |
if(sizeof(TUnicodeOne) == sizeof(BSFL_UE_SURROGATES))
| |
{
| |
uint cp = code;
| |
if(code >= 0xD800 && code <= 0xDBFF) // Esta en rango buscamos otro mas.
| |
{
| |
if(i + 6 < len && in[i + 1] == '\\' && in[i + 2] == 'u')
| |
{
| |
int j = i + 2;
| |
DOMBASEULTIS_U_UNICODE_MAKESTRING_CODE(low, j)
| |
if(low >= 0xDC00 && low <= 0xDFFF)
| |
{
| |
cp = 0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00);
| |
i = j;
| |
| |
//--- emoji
| |
BSFL_UTF8_WRITE_4(cp)
| |
continue;
| |
}
| |
}
| |
| |
//---
| |
// if(sizeof(TStrict) == JSONUNESCAPE_STRICT)
| |
// return -1;
| |
code = 0xFFFD; // huerfano, no hay low surrogate despues
| |
}
| |
else
| |
if(code >= 0xDC00 && code <= 0xDFFF)
| |
{
| |
// if(sizeof(TStrict) == JSONUNESCAPE_STRICT)
| |
// return -1;
| |
code = 0xFFFD;
| |
}
| |
}
| |
// si no lo hay entonces siremp max 0xffff unico
| |
| |
//---
| |
BSFL_UTF8_WRITE_3(code)
| |
continue;
| |
}
| |
case TSN_TABLE_UNESCAPE_INVALID:
| |
return -1; // error
| |
default:
| |
res[pos++] = next;
| |
continue;
| |
}
| |
}
| |
//---
| |
res[pos++] = ch;
| |
}
| |
| |
//---
| |
return ArrayResize(res, pos);
| |
}
| |
//---
| |
}
| |
#endif // BASESPARSERSLAN_UE_MAIN_MQH
| |
//+------------------------------------------------------------------+
|