//+------------------------------------------------------------------+ //| 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 BASESPARSERSLAN_SRC_QUERY_PARSER_MQH #define BASESPARSERSLAN_SRC_QUERY_PARSER_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "AsmDef.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CSLDomQueryCompiler; class CSLDomQueryVm; //--- typedef void(*CSLQueryFunc)(CSLDomQueryVm*); //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CSLDomQueryCompiler { public: //--- Raw uchar m_raw[]; int m_pos; int m_len; int m_offset; //--- Registros (mitad mitad) string m_sreg[TSN_DOMPATH_MAX_SIZE_STR]; int m_sreg_s; DomPathV m_reg[TSN_DOMPATH_MAX_SIZE_REG]; int m_reg_s; protected: //--- Bytecode long m_bytecode[]; int m_bytecode_s; int m_bytecode_r; //--- int m_locked_sreg; //--- Funciones CSLQueryFunc m_funcs[128]; int m_funcs_size; //--- Crea un string int MakeString(int i, const int l); //--- Hashmap intermdio para // que el usuiario guardde sus funciones con un keyu.. y luego se consutla con ese key.. // (bueno con hash) CHashMapFast(string, int) m_hash_func; //--- Del parseo en si // HashMap para mapear los jump CHashMapFast(ulong, int) m_hash_jumps; struct pack(8) PendintHashJump { ulong hash; // hash int bytecode_pos; // Indice del bytecode long mask; // mascara a aplicar al valor int bit_start_mask; }; PendintHashJump m_pendings_hashes[]; int m_pendings_hashes_s; int m_pendings_hashes_r; // Pila de valores tokenizacoin long m_pila_v[ASMPARSER_MAX_VAL_OPS]; uchar m_pila_v_t[ASMPARSER_MAX_VAL_OPS]; int m_pila_v_s; // tamaƱo actual usado de pila // Siguiente token tokenizacion uint m_pila_ins[ASMPARSER_MAX_VAL_OPS]; int m_pila_ins_t; int m_pila_curr; // curr instruction pila // uchar m_next; // sigueinte token uint m_p_f; // flags del token //--- ENUM_ASM_DOMPATH_PARSER_ERR m_last_err_parse; public: CSLDomQueryCompiler(void); ~CSLDomQueryCompiler(void) {} //--- Correct padding void CorrectPadding(); //--- Load __forceinline void CalcLen() { m_len = ArraySize(m_raw); } inline void Assing(const string& raw); int AssingFile(const string& file_name, const bool comon_flag); bool AutoParseFileByExt(const int reg_type); //--- Lockear vlaores previo a los Run void LockValues(); //--- Save bool SaveCompiled(const string & file_name_out, const bool out_common_flag) const; //--- Normalize void NormalizeUft(); //--- Errores void GetLastErrorLocation(int &col, int &line) const; __forceinline ENUM_ASM_DOMPATH_PARSER_ERR LastErr() const { return m_last_err_parse; } //--- Funciones void SetFunction(const string &func_name, CSLQueryFunc func); //--- Compiladores finales bool CompileAsm(); bool CompileQuery() { return true; } // place holder //--- void Summary(); __forceinline int GetStepIns(const ENUM_DOMPATH_QUERY_INS_TYPE t) const; //--- void ParseErrorInfo(bool w_avanze = false) const; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CSLDomQueryCompiler::CSLDomQueryCompiler() : m_bytecode_r(256), m_bytecode_s(0), m_pendings_hashes_s(0), m_pendings_hashes_r(16) , m_funcs_size(0), m_sreg_s(0), m_reg_s(0), m_hash_func(64), m_hash_jumps(64) { ArrayResize(m_bytecode, m_bytecode_r, m_bytecode_r); ArrayResize(m_pendings_hashes, m_pendings_hashes_r, m_pendings_hashes_r); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSLDomQueryCompiler::Summary(void) { Print("---- CSLDomQueryCompiler ----"); Print("-- Bytecode --"); int cur = 0; while(cur < m_bytecode_s) { const ENUM_DOMPATH_QUERY_INS_TYPE t = ENUM_DOMPATH_QUERY_INS_TYPE(m_bytecode[cur] & TSN_DOMPATH_MASK_INS_TYPE); PrintFormat("[%d] %s", cur, EnumToString(t)); cur += GetStepIns(t); } Print("-- Registers --"); Print("Registers str size: ", m_sreg_s); Print("Functions size: ", m_funcs_size); Print("---- end ----"); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ __forceinline int CSLDomQueryCompiler::GetStepIns(const ENUM_DOMPATH_QUERY_INS_TYPE t) const { switch(t) { //--- case DOMPATH_QUERY_INS_TYPE_ACCESO_KEY: case DOMPATH_QUERY_INS_TYPE_ACCESO_KEY_F: return 2; //--- case DOMPATH_QUERY_INS_TYPE_ACCESO_IDX: case DOMPATH_QUERY_INS_TYPE_ACCESO_IDX_F: case DOMPATH_QUERY_INS_TYPE_INICIAR_ITERACION: case DOMPATH_QUERY_INS_TYPE_ITER_ASSING: return 1; //--- case DOMPATH_QUERY_INS_TYPE_EXIST: return 2; //--- case DOMPATH_QUERY_INS_TYPE_SAVE: case DOMPATH_QUERY_INS_TYPE_SAVE_NO_RESET: return 1; //--- case DOMPATH_QUERY_INS_TYPE_M_MUL: case DOMPATH_QUERY_INS_TYPE_M_RES: case DOMPATH_QUERY_INS_TYPE_M_DIV: case DOMPATH_QUERY_INS_TYPE_M_SUM: case DOMPATH_QUERY_INS_TYPE_M_MOD: case DOMPATH_QUERY_INS_TYPE_M_BIT_OR: case DOMPATH_QUERY_INS_TYPE_M_BIT_AND: case DOMPATH_QUERY_INS_TYPE_M_BIT_XOR: case DOMPATH_QUERY_INS_TYPE_M_BIT_DEZ_IZQ: case DOMPATH_QUERY_INS_TYPE_M_BIT_DEZ_DRC: case DOMPATH_QUERY_INS_TYPE_M_BIT_NOT: case DOMPATH_QUERY_INS_TYPE_M_UNARY_NOT: case DOMPATH_QUERY_INS_TYPE_COMPARE_EQ: case DOMPATH_QUERY_INS_TYPE_COMPARE_MENOR: case DOMPATH_QUERY_INS_TYPE_COMPARE_MENOR_EQ: case DOMPATH_QUERY_INS_TYPE_COMPARE_IS: case DOMPATH_QUERY_INS_TYPE_CALL: return 2; case DOMPATH_QUERY_INS_TYPE_RESERVE: return 1; case DOMPATH_QUERY_INS_TYPE_SLICING: return 2; case DOMPATH_QUERY_INS_TYPE_JUMP: case DOMPATH_QUERY_INS_TYPE_COPY_ALL: case DOMPATH_QUERY_INS_TYPE_APPEND: case DOMPATH_QUERY_INS_TYPE_SALIDA: return 1; default: return 1; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSLDomQueryCompiler::LockValues() { // Luego de compilar llamar a esta funcion m_locked_sreg = m_sreg_s; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CSLDomQueryCompiler::AutoParseFileByExt(const int reg_type) { switch(reg_type) { case TSN_DOMPATH_REGTYPE_JSONPB: { return m_bytecode_s > 0; // solo chekeamos si el bytecode cargado es mayor a 0 } case TSN_DOMPATH_REGTYPE_JSONPASM: { CorrectPadding(); return CompileAsm(); } default: { m_last_err_parse = ASM_DOMPATH_PARSER_ERR_INVALID_FILE_EXTENSION; m_pos = 0; return false;; } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CSLDomQueryCompiler::AssingFile(const string &file_name, const bool comon_flag) { const int reg_type = (int)DomPathExtBSPLF_File(file_name); if(reg_type == TSN_DOMPATH_REGTYPE_JSONPB) { // cache ::ResetLastError(); const int fh = FileOpen(file_name, FILE_BIN | FILE_ANSI | (comon_flag ? FILE_COMMON : 0) | FILE_READ); if(fh == INVALID_HANDLE) { return -1; } m_bytecode_s = FileReadInteger(fh); m_bytecode_r = m_bytecode_s; FileReadArray(fh, m_bytecode, 0, m_bytecode_s); m_sreg_s = FileReadInteger(fh); for(int i = 0; i < m_sreg_s; i++) { const int l = FileReadInteger(fh); m_sreg[i] = FileReadString(fh, l); } const int t = FileReadInteger(fh); FileReadArray(fh, m_reg, 0, t); //--- FileClose(fh); //--- return TSN_DOMPATH_REGTYPE_JSONPB; } else { // asm o normal const int l = (int)FileLoad(file_name, m_raw, (comon_flag ? FILE_COMMON : 0)); if(l == -1) return -1; //--- m_len = l; //--- NormalizeUft(); //--- return reg_type; } return 0; // place holder } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CSLDomQueryCompiler::SaveCompiled(const string &file_name_out, const bool out_common_flag) const { //--- const int fh = FileOpen(file_name_out, FILE_WRITE | FILE_BIN | FILE_ANSI | (out_common_flag ? FILE_COMMON : 0)); if(fh == INVALID_HANDLE) { return false; } //--- FileWriteInteger(fh, m_bytecode_s); FileWriteArray(fh, m_bytecode, 0, m_bytecode_s); //--- FileWriteInteger(fh, m_sreg_s); for(int i = 0; i < m_sreg_s; i++) { const int l = StringLen(m_sreg[i]); FileWriteInteger(fh, l); FileWriteString(fh, m_sreg[i], l); } //--- static const int t = TSN_DOMPATH_MAX_SIZE_REG >> 1; FileWriteInteger(fh, t); FileWriteArray(fh, m_reg, 0, t); //--- FileClose(fh); //--- return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSLDomQueryCompiler::SetFunction(const string &func_name, CSLQueryFunc func) { const int idx = m_hash_func.Find(func_name); if(idx == -1) { m_funcs[m_funcs_size] = func; m_hash_func.Add(func_name, m_funcs_size); m_funcs_size++; } else { m_funcs[idx] = func; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline void CSLDomQueryCompiler::Assing(const string& raw) { m_len = StringToCharArray(raw, m_raw); m_offset = 0; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSLDomQueryCompiler::CorrectPadding() { //--- const int prev = m_len; ArrayResize(m_raw, (m_len += 8)); //--- Rellenamos for(int i = prev; i < m_len; i++) { m_raw[i] = '\n'; } //--- Chekeamos if(m_len > m_bytecode_r) { m_bytecode_r = m_len; ArrayResize(m_bytecode, m_len, m_len); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSLDomQueryCompiler::NormalizeUft() { m_offset = 0; //--- if(m_len >= 2) { // UTF16 BE if(m_raw[0] == 0xFF && m_raw[1] == 0xFE) { const int n = (m_len - 2) >> 1; for(int i = 0; i < n; i++) m_raw[i] = m_raw[2 + (i * 2)]; // byte bajo (el char real) m_len = ArrayResize(m_raw, n); } // UTF16 LE else if(m_raw[0] == 0xFE && m_raw[1] == 0xFF) { const int n = (m_len - 2) >> 1; for(int i = 0; i < n; i++) m_raw[i] = m_raw[3 + (i * 2)]; // byte alto (invertido) m_len = ArrayResize(m_raw, n); } } else if(m_len >= 3 && m_raw[0] == 0xEF && m_raw[1] == 0xBB && m_raw[2] == 0xBF) { // UTF-8 BOM m_offset = 3; // simplemente saltear } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CSLDomQueryCompiler::MakeString(int i, const int l) { //--- m_sreg[m_sreg_s].Truncate(l); int k = 0; const int e = i + l; //--- for(; i < e; i++) m_sreg[m_sreg_s].SetChar(k++, m_raw[i]); //--- m_sreg_s++; return m_sreg_s - 1; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSLDomQueryCompiler::GetLastErrorLocation(int &col, int &line) const { col = 1; line = 1; //Print("err:"); //PrintFormat("Val = '%s'", CharArrayToString(m_raw, 0, m_pos)); for(int i = 0; i <= m_pos && i < m_len; i++) { if(m_raw[i] == '\n') { line++; col = 1; } else col++; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSLDomQueryCompiler::ParseErrorInfo(bool w_avanze) const { if(m_last_err_parse == ASM_DOMPATH_PARSER_ERR_NOT) return; //--- int col, line; GetLastErrorLocation(col, line); string err = ""; //--- switch(m_last_err_parse) { case ASM_DOMPATH_PARSER_ERR_MALFORMERD_ASM: err = "ASM mal formado: se encontro un token que no era valido en este punto"; break; case ASM_DOMPATH_PARSER_ERR_INVALID_INSTRUCTION: err = "Instruccion invalida: el nombre de instruccion (INS_*) no existe"; break; case ASM_DOMPATH_PARSER_ERR_INVALID_TOKEN: err = "Token invalido: caracter inesperado, no coincide con ningun token conocido"; break; case ASM_DOMPATH_PARSER_ERR_JUMP_NOT_CLOSED: err = "Etiqueta de salto (@Alias:) sin cerrar antes del final del archivo"; break; case ASM_DOMPATH_PARSER_ERR_INVALID_CASE_INS: err = "Instruccion reconocida pero sin manejo implementado en el compilador (case invalido)"; break; case ASM_DOMPATH_PARSER_ERR_STRING_NOT_CLOSED: err = "Cadena (string) sin comilla de cierre antes del final del archivo"; break; case ASM_DOMPATH_PARSER_ERR_INVALID_JUMP_KEY: err = "Salto a una etiqueta que nunca fue declarada (@Alias: inexistente)"; break; case ASM_DOMPATH_PARSER_ERR_STR_HASH_NOT_CLOSED: err = "Hash de salto (' o *) sin caracter de cierre antes del final del archivo"; break; case ASM_DOMPATH_PARSER_ERR_MALFORMERD_ASM_CTX_GEN: err = "ASM mal formado dentro del contexto de generacion (secuencia de tokens invalida para esta instruccion)"; break; case ASM_DOMPATH_PARSER_ERR_FUNCTION_NOT_EXIST: err = "La funcion invocada (CALL) no fue registrada previamente con SetFunction"; break; case ASM_DOMPATH_PARSER_ERR_COMENTARIO_NO_CERRO_Y_AUN_QUEDA_ALGO: err = "El comentario no cerro correctamente y aun quedaba contenido pendiente de parsear"; break; case ASM_DOMPATH_PARSER_ERR_INVALID_FILE_EXTENSION: err = "Tipo de extension de archivo invalida"; break; default: err = "Error desconocido"; break; } //--- PrintFormat("[DomPath ASM] Error (linea %d, columna %d):\n'%s'", line, col, err); //--- if(w_avanze) { Print("Avanze: "); Print("--------"); Print(CharArrayToString(m_raw, 0, m_pos)); Print("--------"); } } } //+------------------------------------------------------------------+ #endif // BASESPARSERSLAN_SRC_QUERY_PARSER_MQH //+------------------------------------------------------------------+