978 lines
27 KiB
MQL5
978 lines
27 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Qwen3.7Max.mqh |
|
||
|
|
//| Copyright 2026, Niquel Mendoza. |
|
||
|
|
//| https://www.mql5.com/ |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property copyright "Copyright 2026, Niquel Mendoza."
|
||
|
|
#property link "https://www.mql5.com/"
|
||
|
|
#property strict
|
||
|
|
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
// Same prompt claude\mql5 lite
|
||
|
|
/*
|
||
|
|
Primeras impresiones compilo a la primera...
|
||
|
|
Ahora testeesmo..
|
||
|
|
Bien primer testeo y por lo visto errores
|
||
|
|
2026.06.28 21:21:08.983 Qwen3.7Max (EURUSD,H1) Unexpected character
|
||
|
|
Parece que tendremos que corregir.
|
||
|
|
Por lo visto era la vlidacion extra.. al final de terminar el parse ya lo quite..
|
||
|
|
Ahora probemos denuevo..
|
||
|
|
|
||
|
|
2026.06.28 21:27:40.629 Qwen3.7Max (EURUSD,H1) true
|
||
|
|
2026.06.28 21:27:40.629 Qwen3.7Max (EURUSD,H1) 0
|
||
|
|
2026.06.28 21:27:41.401 Qwen3.7Max (EURUSD,H1) true
|
||
|
|
2026.06.28 21:27:41.401 Qwen3.7Max (EURUSD,H1) 0
|
||
|
|
Nada por lo visto
|
||
|
|
|
||
|
|
Print(json.Parse());
|
||
|
|
Print(json["statuses"].Count());
|
||
|
|
|
||
|
|
Fnciona pero count da 0 deberia de dar 100 otra ia que falla aqui.
|
||
|
|
Otra vez mas este modelo chino creo que es mas decente pero aun creo que queda por debajo de claude o gpt..
|
||
|
|
No da... sabe sintaxis pero creo aun le falta
|
||
|
|
.. en total tardo 15 minutos daod que razono como 10 literalmente...
|
||
|
|
Podria decirle que lo rehaga...
|
||
|
|
*/
|
||
|
|
|
||
|
|
//// Code ai start..
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Json.mqh |
|
||
|
|
//| High Performance JSON Parser for MQL5 |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property copyright "High Performance JSON Parser"
|
||
|
|
#property version "1.00"
|
||
|
|
#property strict
|
||
|
|
|
||
|
|
#ifndef __JSON_MQH__
|
||
|
|
#define __JSON_MQH__
|
||
|
|
|
||
|
|
//--- Enumeraciones para tipos de datos y errores
|
||
|
|
enum ENUM_JSON_TYPE
|
||
|
|
{
|
||
|
|
JSON_UNDEFINED = -1,
|
||
|
|
JSON_OBJECT = 0,
|
||
|
|
JSON_ARRAY,
|
||
|
|
JSON_STRING,
|
||
|
|
JSON_NUMBER,
|
||
|
|
JSON_BOOL,
|
||
|
|
JSON_NULL
|
||
|
|
};
|
||
|
|
|
||
|
|
enum ENUM_JSON_ERROR
|
||
|
|
{
|
||
|
|
JSON_OK = 0,
|
||
|
|
JSON_UNEXPECTED_CHARACTER,
|
||
|
|
JSON_UNEXPECTED_END,
|
||
|
|
JSON_INVALID_NUMBER,
|
||
|
|
JSON_INVALID_STRING,
|
||
|
|
JSON_INVALID_ESCAPE,
|
||
|
|
JSON_INVALID_UNICODE,
|
||
|
|
JSON_EXPECTED_COLON,
|
||
|
|
JSON_EXPECTED_COMMA,
|
||
|
|
JSON_EXPECTED_OBJECT,
|
||
|
|
JSON_EXPECTED_ARRAY,
|
||
|
|
JSON_EXPECTED_STRING,
|
||
|
|
JSON_MAX_DEPTH,
|
||
|
|
JSON_UNKNOWN
|
||
|
|
};
|
||
|
|
|
||
|
|
//--- Declaración adelantada
|
||
|
|
class CJsonNode;
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Clase Principal del Parser (CJson) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
class CJson
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
// Buffer público para que el usuario pueda llenarlo directamente sin copias
|
||
|
|
uchar Buffer[];
|
||
|
|
|
||
|
|
private:
|
||
|
|
// --- Representación Interna (Structure of Arrays para máximo rendimiento y cache-friendly) ---
|
||
|
|
int m_type[];
|
||
|
|
int m_start[]; // Offset de inicio en Buffer (o valor para bool)
|
||
|
|
int m_end[]; // Offset de fin en Buffer
|
||
|
|
int m_child[]; // Índice del primer hijo
|
||
|
|
int m_next[]; // Índice del siguiente hermano
|
||
|
|
int m_key_start[]; // Offset de inicio de la clave (para objetos)
|
||
|
|
int m_key_end[]; // Offset de fin de la clave
|
||
|
|
|
||
|
|
int m_node_count;
|
||
|
|
int m_node_capacity;
|
||
|
|
|
||
|
|
// --- Stack manual para parsing iterativo ---
|
||
|
|
enum ENUM_STATE
|
||
|
|
{
|
||
|
|
STATE_ROOT,
|
||
|
|
STATE_ARRAY_ITEM,
|
||
|
|
STATE_ARRAY_NEXT,
|
||
|
|
STATE_OBJECT_KEY,
|
||
|
|
STATE_OBJECT_COLON,
|
||
|
|
STATE_OBJECT_VALUE,
|
||
|
|
STATE_OBJECT_NEXT
|
||
|
|
};
|
||
|
|
|
||
|
|
int m_stack_state[];
|
||
|
|
int m_stack_node[];
|
||
|
|
int m_stack_last_child[];
|
||
|
|
int m_stack_key_start[];
|
||
|
|
int m_stack_key_end[];
|
||
|
|
|
||
|
|
int m_stack_top;
|
||
|
|
int m_stack_capacity;
|
||
|
|
|
||
|
|
// --- Estado del Parser ---
|
||
|
|
int m_root;
|
||
|
|
int m_pos;
|
||
|
|
int m_len;
|
||
|
|
|
||
|
|
// --- Manejo de Errores ---
|
||
|
|
ENUM_JSON_ERROR m_error;
|
||
|
|
int m_error_pos;
|
||
|
|
int m_error_line;
|
||
|
|
int m_error_col;
|
||
|
|
|
||
|
|
// --- Métodos Internos ---
|
||
|
|
void GrowNodes()
|
||
|
|
{
|
||
|
|
m_node_capacity = (m_node_capacity == 0) ? 1024 : m_node_capacity * 2;
|
||
|
|
ArrayResize(m_type, m_node_capacity);
|
||
|
|
ArrayResize(m_start, m_node_capacity);
|
||
|
|
ArrayResize(m_end, m_node_capacity);
|
||
|
|
ArrayResize(m_child, m_node_capacity);
|
||
|
|
ArrayResize(m_next, m_node_capacity);
|
||
|
|
ArrayResize(m_key_start, m_node_capacity);
|
||
|
|
ArrayResize(m_key_end, m_node_capacity);
|
||
|
|
}
|
||
|
|
|
||
|
|
void GrowStack()
|
||
|
|
{
|
||
|
|
m_stack_capacity = (m_stack_capacity == 0) ? 128 : m_stack_capacity * 2;
|
||
|
|
ArrayResize(m_stack_state, m_stack_capacity);
|
||
|
|
ArrayResize(m_stack_node, m_stack_capacity);
|
||
|
|
ArrayResize(m_stack_last_child, m_stack_capacity);
|
||
|
|
ArrayResize(m_stack_key_start, m_stack_capacity);
|
||
|
|
ArrayResize(m_stack_key_end, m_stack_capacity);
|
||
|
|
}
|
||
|
|
|
||
|
|
int AddNode(int type, int start, int end)
|
||
|
|
{
|
||
|
|
if(m_node_count >= m_node_capacity)
|
||
|
|
GrowNodes();
|
||
|
|
int idx = m_node_count++;
|
||
|
|
m_type[idx] = type;
|
||
|
|
m_start[idx] = start;
|
||
|
|
m_end[idx] = end;
|
||
|
|
m_child[idx] = -1;
|
||
|
|
m_next[idx] = -1;
|
||
|
|
m_key_start[idx] = -1;
|
||
|
|
m_key_end[idx] = -1;
|
||
|
|
return idx;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Error(ENUM_JSON_ERROR err)
|
||
|
|
{
|
||
|
|
if(m_error == JSON_OK) // Solo registrar el primer error
|
||
|
|
{
|
||
|
|
m_error = err;
|
||
|
|
m_error_pos = m_pos;
|
||
|
|
m_error_line = 1;
|
||
|
|
m_error_col = 1;
|
||
|
|
for(int i = 0; i < m_pos && i < m_len; i++)
|
||
|
|
{
|
||
|
|
if(Buffer[i] == '\n')
|
||
|
|
{
|
||
|
|
m_error_line++;
|
||
|
|
m_error_col = 1;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
m_error_col++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void SkipWhitespace()
|
||
|
|
{
|
||
|
|
while(m_pos < m_len)
|
||
|
|
{
|
||
|
|
uchar c = Buffer[m_pos];
|
||
|
|
if(c < 33)
|
||
|
|
m_pos++; // MEJORADO POR LEO
|
||
|
|
else
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ParseString(int &start, int &end)
|
||
|
|
{
|
||
|
|
m_pos++; // Saltar la comilla inicial '"'
|
||
|
|
start = m_pos;
|
||
|
|
while(m_pos < m_len)
|
||
|
|
{
|
||
|
|
uchar c = Buffer[m_pos];
|
||
|
|
if(c == '"')
|
||
|
|
{
|
||
|
|
end = m_pos;
|
||
|
|
m_pos++;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if(c == '\\') // MEJORADO POR LEO
|
||
|
|
{
|
||
|
|
m_pos += 2;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
if(c < 32)
|
||
|
|
{
|
||
|
|
Error(JSON_INVALID_STRING); // Caracteres de control no escapados
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
m_pos++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Error(JSON_UNEXPECTED_END);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ParseNumber(int &start, int &end)
|
||
|
|
{
|
||
|
|
start = m_pos;
|
||
|
|
if(Buffer[m_pos] == '-')
|
||
|
|
m_pos++;
|
||
|
|
if(m_pos >= m_len)
|
||
|
|
{
|
||
|
|
Error(JSON_INVALID_NUMBER);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(Buffer[m_pos] == '0')
|
||
|
|
{
|
||
|
|
m_pos++;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(Buffer[m_pos] >= '1' && Buffer[m_pos] <= '9')
|
||
|
|
{
|
||
|
|
while(m_pos < m_len && Buffer[m_pos] >= '0' && Buffer[m_pos] <= '9')
|
||
|
|
m_pos++;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
Error(JSON_INVALID_NUMBER);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(m_pos < m_len && Buffer[m_pos] == '.')
|
||
|
|
{
|
||
|
|
m_pos++;
|
||
|
|
if(m_pos >= m_len || Buffer[m_pos] < '0' || Buffer[m_pos] > '9')
|
||
|
|
{
|
||
|
|
Error(JSON_INVALID_NUMBER);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
while(m_pos < m_len && Buffer[m_pos] >= '0' && Buffer[m_pos] <= '9')
|
||
|
|
m_pos++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(m_pos < m_len && (Buffer[m_pos] == 'e' || Buffer[m_pos] == 'E'))
|
||
|
|
{
|
||
|
|
m_pos++;
|
||
|
|
if(m_pos < m_len && (Buffer[m_pos] == '+' || Buffer[m_pos] == '-'))
|
||
|
|
m_pos++;
|
||
|
|
if(m_pos >= m_len || Buffer[m_pos] < '0' || Buffer[m_pos] > '9')
|
||
|
|
{
|
||
|
|
Error(JSON_INVALID_NUMBER);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
while(m_pos < m_len && Buffer[m_pos] >= '0' && Buffer[m_pos] <= '9')
|
||
|
|
m_pos++;
|
||
|
|
}
|
||
|
|
|
||
|
|
end = m_pos;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
int ParseValue()
|
||
|
|
{
|
||
|
|
SkipWhitespace();
|
||
|
|
if(m_pos >= m_len)
|
||
|
|
{
|
||
|
|
Error(JSON_UNEXPECTED_END);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
uchar c = Buffer[m_pos];
|
||
|
|
|
||
|
|
if(c == '"')
|
||
|
|
{
|
||
|
|
int start, end;
|
||
|
|
if(!ParseString(start, end))
|
||
|
|
return -1;
|
||
|
|
return AddNode(JSON_STRING, start, end);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(c == '{')
|
||
|
|
{
|
||
|
|
m_pos++;
|
||
|
|
int node = AddNode(JSON_OBJECT, m_pos - 1, -1);
|
||
|
|
PushState(STATE_OBJECT_KEY, node);
|
||
|
|
return node;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(c == '[')
|
||
|
|
{
|
||
|
|
m_pos++;
|
||
|
|
int node = AddNode(JSON_ARRAY, m_pos - 1, -1);
|
||
|
|
PushState(STATE_ARRAY_ITEM, node);
|
||
|
|
return node;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(c == 't')
|
||
|
|
{
|
||
|
|
if(m_pos + 4 <= m_len && Buffer[m_pos + 1] == 'r' && Buffer[m_pos + 2] == 'u' && Buffer[m_pos + 3] == 'e')
|
||
|
|
{
|
||
|
|
m_pos += 4;
|
||
|
|
return AddNode(JSON_BOOL, 1, -1); // 1 = true
|
||
|
|
}
|
||
|
|
Error(JSON_UNEXPECTED_CHARACTER);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(c == 'f')
|
||
|
|
{
|
||
|
|
if(m_pos + 5 <= m_len && Buffer[m_pos + 1] == 'a' && Buffer[m_pos + 2] == 'l' && Buffer[m_pos + 3] == 's' && Buffer[m_pos + 4] == 'e')
|
||
|
|
{
|
||
|
|
m_pos += 5;
|
||
|
|
return AddNode(JSON_BOOL, 0, -1); // 0 = false
|
||
|
|
}
|
||
|
|
Error(JSON_UNEXPECTED_CHARACTER);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(c == 'n')
|
||
|
|
{
|
||
|
|
if(m_pos + 4 <= m_len && Buffer[m_pos + 1] == 'u' && Buffer[m_pos + 2] == 'l' && Buffer[m_pos + 3] == 'l')
|
||
|
|
{
|
||
|
|
m_pos += 4;
|
||
|
|
return AddNode(JSON_NULL, 0, -1);
|
||
|
|
}
|
||
|
|
Error(JSON_UNEXPECTED_CHARACTER);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(c == '-' || (c >= '0' && c <= '9'))
|
||
|
|
{
|
||
|
|
int start, end;
|
||
|
|
if(!ParseNumber(start, end))
|
||
|
|
return -1;
|
||
|
|
return AddNode(JSON_NUMBER, start, end);
|
||
|
|
}
|
||
|
|
|
||
|
|
Error(JSON_UNEXPECTED_CHARACTER);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
void PushState(int state, int node_idx)
|
||
|
|
{
|
||
|
|
if(m_stack_top >= m_stack_capacity)
|
||
|
|
GrowStack();
|
||
|
|
m_stack_state[m_stack_top] = state;
|
||
|
|
m_stack_node[m_stack_top] = node_idx;
|
||
|
|
m_stack_last_child[m_stack_top] = -1;
|
||
|
|
m_stack_key_start[m_stack_top] = -1;
|
||
|
|
m_stack_key_end[m_stack_top] = -1;
|
||
|
|
m_stack_top++;
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
CJson()
|
||
|
|
{
|
||
|
|
m_node_count = 0;
|
||
|
|
m_node_capacity = 0;
|
||
|
|
m_stack_top = 0;
|
||
|
|
m_stack_capacity = 0;
|
||
|
|
m_root = -1;
|
||
|
|
Reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
~CJson() { Clear(); }
|
||
|
|
|
||
|
|
bool Parse()
|
||
|
|
{
|
||
|
|
Reset();
|
||
|
|
m_len = ArraySize(Buffer);
|
||
|
|
if(m_len == 0)
|
||
|
|
{
|
||
|
|
Error(JSON_UNEXPECTED_END);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
PushState(STATE_ROOT, -1);
|
||
|
|
|
||
|
|
while(m_stack_top > 0)
|
||
|
|
{
|
||
|
|
int state = m_stack_state[m_stack_top - 1];
|
||
|
|
SkipWhitespace();
|
||
|
|
|
||
|
|
if(m_pos >= m_len && state != STATE_ROOT)
|
||
|
|
{
|
||
|
|
Error(JSON_UNEXPECTED_END);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
uchar c = (m_pos < m_len) ? Buffer[m_pos] : 0;
|
||
|
|
|
||
|
|
if(state == STATE_ROOT)
|
||
|
|
{
|
||
|
|
m_root = ParseValue();
|
||
|
|
if(m_root == -1)
|
||
|
|
break;
|
||
|
|
m_stack_top--; // Pop root
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(state == STATE_ARRAY_ITEM || state == STATE_ARRAY_NEXT)
|
||
|
|
{
|
||
|
|
if(state == STATE_ARRAY_NEXT)
|
||
|
|
{
|
||
|
|
if(c == ',')
|
||
|
|
{
|
||
|
|
m_pos++;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if(c == ']')
|
||
|
|
{
|
||
|
|
m_pos++;
|
||
|
|
m_stack_top--;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
Error(JSON_EXPECTED_COMMA);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
// STATE_ARRAY_ITEM
|
||
|
|
if(c == ']') // Array vacío
|
||
|
|
{
|
||
|
|
m_pos++;
|
||
|
|
m_stack_top--;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
int child = ParseValue();
|
||
|
|
if(child == -1)
|
||
|
|
break;
|
||
|
|
|
||
|
|
int arr_node = m_stack_node[m_stack_top - 1];
|
||
|
|
if(m_child[arr_node] == -1)
|
||
|
|
{
|
||
|
|
m_child[arr_node] = child;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
m_next[m_stack_last_child[m_stack_top - 1]] = child;
|
||
|
|
}
|
||
|
|
m_stack_last_child[m_stack_top - 1] = child;
|
||
|
|
m_stack_state[m_stack_top - 1] = STATE_ARRAY_NEXT;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(state == STATE_OBJECT_KEY || state == STATE_OBJECT_NEXT)
|
||
|
|
{
|
||
|
|
if(state == STATE_OBJECT_NEXT)
|
||
|
|
{
|
||
|
|
if(c == ',')
|
||
|
|
{
|
||
|
|
m_pos++;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if(c == '}')
|
||
|
|
{
|
||
|
|
m_pos++;
|
||
|
|
m_stack_top--;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
Error(JSON_EXPECTED_COMMA);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
// STATE_OBJECT_KEY
|
||
|
|
if(c == '}') // Objeto vacío
|
||
|
|
{
|
||
|
|
m_pos++;
|
||
|
|
m_stack_top--;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if(c != '"')
|
||
|
|
{
|
||
|
|
Error(JSON_EXPECTED_STRING);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
int ks, ke;
|
||
|
|
if(!ParseString(ks, ke))
|
||
|
|
break;
|
||
|
|
m_stack_key_start[m_stack_top - 1] = ks;
|
||
|
|
m_stack_key_end[m_stack_top - 1] = ke;
|
||
|
|
m_stack_state[m_stack_top - 1] = STATE_OBJECT_COLON;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(state == STATE_OBJECT_COLON)
|
||
|
|
{
|
||
|
|
if(c != ':')
|
||
|
|
{
|
||
|
|
Error(JSON_EXPECTED_COLON);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
m_pos++;
|
||
|
|
m_stack_state[m_stack_top - 1] = STATE_OBJECT_VALUE;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(state == STATE_OBJECT_VALUE)
|
||
|
|
{
|
||
|
|
int child = ParseValue();
|
||
|
|
if(child == -1)
|
||
|
|
break;
|
||
|
|
int obj_node = m_stack_node[m_stack_top - 1];
|
||
|
|
|
||
|
|
m_key_start[child] = m_stack_key_start[m_stack_top - 1];
|
||
|
|
m_key_end[child] = m_stack_key_end[m_stack_top - 1];
|
||
|
|
|
||
|
|
if(m_child[obj_node] == -1)
|
||
|
|
{
|
||
|
|
m_child[obj_node] = child;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
m_next[m_stack_last_child[m_stack_top - 1]] = child;
|
||
|
|
}
|
||
|
|
m_stack_last_child[m_stack_top - 1] = child;
|
||
|
|
m_stack_state[m_stack_top - 1] = STATE_OBJECT_NEXT;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
if(m_error == JSON_OK) {
|
||
|
|
SkipWhitespace();
|
||
|
|
if(m_pos < m_len) {
|
||
|
|
Error(JSON_UNEXPECTED_CHARACTER); // Caracteres basura al final
|
||
|
|
}
|
||
|
|
}*/
|
||
|
|
return (m_error == JSON_OK);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Reset()
|
||
|
|
{
|
||
|
|
m_node_count = 0;
|
||
|
|
m_stack_top = 0;
|
||
|
|
m_root = -1;
|
||
|
|
m_pos = 0;
|
||
|
|
m_error = JSON_OK;
|
||
|
|
m_error_pos = 0;
|
||
|
|
m_error_line = 0;
|
||
|
|
m_error_col = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Clear()
|
||
|
|
{
|
||
|
|
Reset();
|
||
|
|
ArrayFree(m_type);
|
||
|
|
ArrayFree(m_start);
|
||
|
|
ArrayFree(m_end);
|
||
|
|
ArrayFree(m_child);
|
||
|
|
ArrayFree(m_next);
|
||
|
|
ArrayFree(m_key_start);
|
||
|
|
ArrayFree(m_key_end);
|
||
|
|
ArrayFree(m_stack_state);
|
||
|
|
ArrayFree(m_stack_node);
|
||
|
|
ArrayFree(m_stack_last_child);
|
||
|
|
ArrayFree(m_stack_key_start);
|
||
|
|
ArrayFree(m_stack_key_end);
|
||
|
|
m_node_capacity = 0;
|
||
|
|
m_stack_capacity = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- API de Errores ---
|
||
|
|
ENUM_JSON_ERROR GetError() { return m_error; }
|
||
|
|
bool HasError() { return m_error != JSON_OK; }
|
||
|
|
int GetErrorPosition() { return m_error_pos; }
|
||
|
|
int GetLine() { return m_error_line; }
|
||
|
|
int GetColumn() { return m_error_col; }
|
||
|
|
|
||
|
|
string GetErrorMessage()
|
||
|
|
{
|
||
|
|
switch(m_error)
|
||
|
|
{
|
||
|
|
case JSON_OK:
|
||
|
|
return "OK";
|
||
|
|
case JSON_UNEXPECTED_CHARACTER:
|
||
|
|
return "Unexpected character";
|
||
|
|
case JSON_UNEXPECTED_END:
|
||
|
|
return "Unexpected end of JSON";
|
||
|
|
case JSON_INVALID_NUMBER:
|
||
|
|
return "Invalid number";
|
||
|
|
case JSON_INVALID_STRING:
|
||
|
|
return "Invalid string";
|
||
|
|
case JSON_INVALID_ESCAPE:
|
||
|
|
return "Invalid escape sequence";
|
||
|
|
case JSON_INVALID_UNICODE:
|
||
|
|
return "Invalid unicode escape";
|
||
|
|
case JSON_EXPECTED_COLON:
|
||
|
|
return "Expected ':'";
|
||
|
|
case JSON_EXPECTED_COMMA:
|
||
|
|
return "Expected ','";
|
||
|
|
case JSON_EXPECTED_OBJECT:
|
||
|
|
return "Expected object";
|
||
|
|
case JSON_EXPECTED_ARRAY:
|
||
|
|
return "Expected array";
|
||
|
|
case JSON_EXPECTED_STRING:
|
||
|
|
return "Expected string";
|
||
|
|
case JSON_MAX_DEPTH:
|
||
|
|
return "Maximum nesting depth exceeded";
|
||
|
|
default:
|
||
|
|
return "Unknown error";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- API de Acceso (Internos) ---
|
||
|
|
int GetRoot() { return m_root; }
|
||
|
|
int GetNodeType(int idx) { return (idx >= 0 && idx < m_node_count) ? m_type[idx] : JSON_UNDEFINED; }
|
||
|
|
int GetNodeChild(int idx) { return (idx >= 0) ? m_child[idx] : -1; }
|
||
|
|
int GetNodeNext(int idx) { return (idx >= 0) ? m_next[idx] : -1; }
|
||
|
|
int GetNodeStart(int idx) { return (idx >= 0) ? m_start[idx] : -1; }
|
||
|
|
int GetNodeEnd(int idx) { return (idx >= 0) ? m_end[idx] : -1; }
|
||
|
|
int GetNodeCount() { return m_node_count; }
|
||
|
|
int GetKeyStart(int idx) { return (idx >= 0) ? m_key_start[idx] : -1; }
|
||
|
|
int GetKeyEnd(int idx) { return (idx >= 0) ? m_key_end[idx] : -1; }
|
||
|
|
|
||
|
|
// Conversión rápida de String (Decodificación bajo demanda)
|
||
|
|
string GetStringValue(int start, int end)
|
||
|
|
{
|
||
|
|
if(start < 0 || end < start)
|
||
|
|
return "";
|
||
|
|
bool has_escapes = false;
|
||
|
|
for(int i = start; i < end; i++)
|
||
|
|
{
|
||
|
|
if(Buffer[i] == '\\')
|
||
|
|
{
|
||
|
|
has_escapes = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if(!has_escapes)
|
||
|
|
{
|
||
|
|
return CharArrayToString(Buffer, start, end - start);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Decodificación de escapes (Rama lenta)
|
||
|
|
uchar decoded[];
|
||
|
|
ArrayResize(decoded, end - start);
|
||
|
|
int dec_pos = 0;
|
||
|
|
for(int i = start; i < end; i++)
|
||
|
|
{
|
||
|
|
if(Buffer[i] == '\\' && i + 1 < end)
|
||
|
|
{
|
||
|
|
i++;
|
||
|
|
uchar esc = Buffer[i];
|
||
|
|
if(esc == '"')
|
||
|
|
decoded[dec_pos++] = '"';
|
||
|
|
else
|
||
|
|
if(esc == '\\')
|
||
|
|
decoded[dec_pos++] = '\\';
|
||
|
|
else
|
||
|
|
if(esc == '/')
|
||
|
|
decoded[dec_pos++] = '/';
|
||
|
|
else
|
||
|
|
if(esc == 'b')
|
||
|
|
decoded[dec_pos++] = 8;
|
||
|
|
else
|
||
|
|
if(esc == 'f')
|
||
|
|
decoded[dec_pos++] = 12;
|
||
|
|
else
|
||
|
|
if(esc == 'n')
|
||
|
|
decoded[dec_pos++] = 10;
|
||
|
|
else
|
||
|
|
if(esc == 'r')
|
||
|
|
decoded[dec_pos++] = 13;
|
||
|
|
else
|
||
|
|
if(esc == 't')
|
||
|
|
decoded[dec_pos++] = 9;
|
||
|
|
else
|
||
|
|
if(esc == 'u' && i + 4 < end)
|
||
|
|
{
|
||
|
|
int hex = 0;
|
||
|
|
for(int j = 0; j < 4; j++)
|
||
|
|
{
|
||
|
|
hex <<= 4;
|
||
|
|
uchar h = Buffer[i + 1 + j];
|
||
|
|
if(h >= '0' && h <= '9')
|
||
|
|
hex |= (h - '0');
|
||
|
|
else
|
||
|
|
if(h >= 'a' && h <= 'f')
|
||
|
|
hex |= (h - 'a' + 10);
|
||
|
|
else
|
||
|
|
if(h >= 'A' && h <= 'F')
|
||
|
|
hex |= (h - 'A' + 10);
|
||
|
|
}
|
||
|
|
if(hex < 128)
|
||
|
|
decoded[dec_pos++] = (uchar)hex;
|
||
|
|
else
|
||
|
|
if(hex < 0x800)
|
||
|
|
{
|
||
|
|
decoded[dec_pos++] = (uchar)(0xC0 | (hex >> 6));
|
||
|
|
decoded[dec_pos++] = (uchar)(0x80 | (hex & 0x3F));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
decoded[dec_pos++] = (uchar)(0xE0 | (hex >> 12));
|
||
|
|
decoded[dec_pos++] = (uchar)(0x80 | ((hex >> 6) & 0x3F));
|
||
|
|
decoded[dec_pos++] = (uchar)(0x80 | (hex & 0x3F));
|
||
|
|
}
|
||
|
|
i += 4;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
decoded[dec_pos++] = Buffer[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return CharArrayToString(decoded, 0, dec_pos);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Conversión rápida de Número (Matemática directa, sin StringToDouble)
|
||
|
|
double GetNumberValue(int start, int end)
|
||
|
|
{
|
||
|
|
double val = 0.0;
|
||
|
|
bool neg = false;
|
||
|
|
int i = start;
|
||
|
|
if(i < end && Buffer[i] == '-')
|
||
|
|
{
|
||
|
|
neg = true;
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
while(i < end && Buffer[i] >= '0' && Buffer[i] <= '9')
|
||
|
|
{
|
||
|
|
val = val * 10.0 + (Buffer[i] - '0');
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
if(i < end && Buffer[i] == '.')
|
||
|
|
{
|
||
|
|
i++;
|
||
|
|
double frac = 0.0, div = 1.0;
|
||
|
|
while(i < end && Buffer[i] >= '0' && Buffer[i] <= '9')
|
||
|
|
{
|
||
|
|
frac = frac * 10.0 + (Buffer[i] - '0');
|
||
|
|
div *= 10.0;
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
val += frac / div;
|
||
|
|
}
|
||
|
|
if(i < end && (Buffer[i] == 'e' || Buffer[i] == 'E'))
|
||
|
|
{
|
||
|
|
i++;
|
||
|
|
bool exp_neg = false;
|
||
|
|
if(i < end && Buffer[i] == '-')
|
||
|
|
{
|
||
|
|
exp_neg = true;
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
if(i < end && Buffer[i] == '+')
|
||
|
|
{
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
int exp = 0;
|
||
|
|
while(i < end && Buffer[i] >= '0' && Buffer[i] <= '9')
|
||
|
|
{
|
||
|
|
exp = exp * 10 + (Buffer[i] - '0');
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
val = val * MathPow(10.0, exp_neg ? -exp : exp);
|
||
|
|
}
|
||
|
|
return neg ? -val : val;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool CompareKey(int child_idx, const string key)
|
||
|
|
{
|
||
|
|
int ks = m_key_start[child_idx];
|
||
|
|
int ke = m_key_end[child_idx];
|
||
|
|
if(ks == -1 || ke == -1)
|
||
|
|
return false;
|
||
|
|
int len = ke - ks;
|
||
|
|
if(len != StringLen(key))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
// Fast path para claves ASCII
|
||
|
|
for(int i = 0; i < len; i++)
|
||
|
|
{
|
||
|
|
ushort ch = StringGetCharacter(key, i);
|
||
|
|
if(ch > 127 || Buffer[ks + i] != (uchar)ch)
|
||
|
|
{
|
||
|
|
return (GetStringValue(ks, ke) == key); // Slow path
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- Operadores para acceso tipo diccionario ---
|
||
|
|
CJsonNode operator[](const string key);
|
||
|
|
CJsonNode operator[](const int index);
|
||
|
|
};
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Clase Nodo (CJsonNode) - Wrapper para acceso fluido |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
class CJsonNode
|
||
|
|
{
|
||
|
|
private:
|
||
|
|
CJson* m_owner;
|
||
|
|
int m_idx;
|
||
|
|
|
||
|
|
public:
|
||
|
|
CJsonNode() : m_owner(NULL), m_idx(-1) {}
|
||
|
|
CJsonNode(CJson* owner, int idx) : m_owner(owner), m_idx(idx) {}
|
||
|
|
|
||
|
|
bool IsValid() { return (m_owner != NULL && m_idx >= 0); }
|
||
|
|
bool Exists() { return IsValid() && m_owner.GetNodeType(m_idx) != JSON_UNDEFINED; }
|
||
|
|
|
||
|
|
CJsonNode operator[](const string key)
|
||
|
|
{
|
||
|
|
if(!IsValid())
|
||
|
|
return CJsonNode();
|
||
|
|
if(m_owner.GetNodeType(m_idx) != JSON_OBJECT)
|
||
|
|
return CJsonNode();
|
||
|
|
int child = m_owner.GetNodeChild(m_idx);
|
||
|
|
while(child != -1)
|
||
|
|
{
|
||
|
|
if(m_owner.CompareKey(child, key))
|
||
|
|
return CJsonNode(m_owner, child);
|
||
|
|
child = m_owner.GetNodeNext(child);
|
||
|
|
}
|
||
|
|
return CJsonNode();
|
||
|
|
}
|
||
|
|
|
||
|
|
CJsonNode operator[](const int index)
|
||
|
|
{
|
||
|
|
if(!IsValid())
|
||
|
|
return CJsonNode();
|
||
|
|
if(m_owner.GetNodeType(m_idx) != JSON_ARRAY)
|
||
|
|
return CJsonNode();
|
||
|
|
int child = m_owner.GetNodeChild(m_idx);
|
||
|
|
int i = 0;
|
||
|
|
while(child != -1)
|
||
|
|
{
|
||
|
|
if(i == index)
|
||
|
|
return CJsonNode(m_owner, child);
|
||
|
|
child = m_owner.GetNodeNext(child);
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
return CJsonNode();
|
||
|
|
}
|
||
|
|
|
||
|
|
string AsString()
|
||
|
|
{
|
||
|
|
if(!IsValid() || m_owner.GetNodeType(m_idx) != JSON_STRING)
|
||
|
|
return "";
|
||
|
|
return m_owner.GetStringValue(m_owner.GetNodeStart(m_idx), m_owner.GetNodeEnd(m_idx));
|
||
|
|
}
|
||
|
|
|
||
|
|
double AsDouble()
|
||
|
|
{
|
||
|
|
if(!IsValid())
|
||
|
|
return 0.0;
|
||
|
|
int t = m_owner.GetNodeType(m_idx);
|
||
|
|
if(t == JSON_NUMBER)
|
||
|
|
return m_owner.GetNumberValue(m_owner.GetNodeStart(m_idx), m_owner.GetNodeEnd(m_idx));
|
||
|
|
if(t == JSON_STRING)
|
||
|
|
return StringToDouble(AsString());
|
||
|
|
return 0.0;
|
||
|
|
}
|
||
|
|
|
||
|
|
long AsLong() { return (long)AsDouble(); }
|
||
|
|
|
||
|
|
bool AsBool()
|
||
|
|
{
|
||
|
|
if(!IsValid())
|
||
|
|
return false;
|
||
|
|
int t = m_owner.GetNodeType(m_idx);
|
||
|
|
if(t == JSON_BOOL)
|
||
|
|
return m_owner.GetNodeStart(m_idx) == 1;
|
||
|
|
if(t == JSON_NUMBER)
|
||
|
|
return AsDouble() != 0.0;
|
||
|
|
if(t == JSON_STRING)
|
||
|
|
return AsString() != "";
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
int Size() { return Count(); }
|
||
|
|
int Count()
|
||
|
|
{
|
||
|
|
if(!IsValid())
|
||
|
|
return 0;
|
||
|
|
int t = m_owner.GetNodeType(m_idx);
|
||
|
|
if(t != JSON_ARRAY && t != JSON_OBJECT)
|
||
|
|
return 0;
|
||
|
|
int count = 0;
|
||
|
|
int child = m_owner.GetNodeChild(m_idx);
|
||
|
|
while(child != -1)
|
||
|
|
{
|
||
|
|
count++;
|
||
|
|
child = m_owner.GetNodeNext(child);
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool IsObject() { return IsValid() && m_owner.GetNodeType(m_idx) == JSON_OBJECT; }
|
||
|
|
bool IsArray() { return IsValid() && m_owner.GetNodeType(m_idx) == JSON_ARRAY; }
|
||
|
|
bool IsString() { return IsValid() && m_owner.GetNodeType(m_idx) == JSON_STRING; }
|
||
|
|
bool IsNumber() { return IsValid() && m_owner.GetNodeType(m_idx) == JSON_NUMBER; }
|
||
|
|
bool IsBool() { return IsValid() && m_owner.GetNodeType(m_idx) == JSON_BOOL; }
|
||
|
|
bool IsNull() { return IsValid() && m_owner.GetNodeType(m_idx) == JSON_NULL; }
|
||
|
|
|
||
|
|
string GetKey()
|
||
|
|
{
|
||
|
|
if(!IsValid())
|
||
|
|
return "";
|
||
|
|
int ks = m_owner.GetKeyStart(m_idx);
|
||
|
|
int ke = m_owner.GetKeyEnd(m_idx);
|
||
|
|
if(ks == -1)
|
||
|
|
return "";
|
||
|
|
return m_owner.GetStringValue(ks, ke);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Implementación de Operadores de CJson |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
CJsonNode CJson::operator[](const string key)
|
||
|
|
{
|
||
|
|
if(m_root == -1 || m_type[m_root] != JSON_OBJECT)
|
||
|
|
return CJsonNode(NULL, -1);
|
||
|
|
int child = m_child[m_root];
|
||
|
|
while(child != -1)
|
||
|
|
{
|
||
|
|
if(CompareKey(child, key))
|
||
|
|
return CJsonNode(GetPointer(this), child);
|
||
|
|
child = m_next[child];
|
||
|
|
}
|
||
|
|
return CJsonNode(NULL, -1);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
CJsonNode CJson::operator[](const int index)
|
||
|
|
{
|
||
|
|
if(m_root == -1 || m_type[m_root] != JSON_ARRAY)
|
||
|
|
return CJsonNode(NULL, -1);
|
||
|
|
int child = m_child[m_root];
|
||
|
|
int i = 0;
|
||
|
|
while(child != -1)
|
||
|
|
{
|
||
|
|
if(i == index)
|
||
|
|
return CJsonNode(GetPointer(this), child);
|
||
|
|
child = m_next[child];
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
return CJsonNode(NULL, -1);
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif // __JSON_MQH__
|
||
|
|
//+------------------------------------------------------------------+
|