372 lines
10 KiB
MQL5
372 lines
10 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Tokenizer.mqh |
|
|
//| Copyright 2026, Niquel Mendoza. |
|
|
//| https://www.mql5.com/es/users/nique_372 |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2026, Niquel Mendoza."
|
|
#property link "https://www.mql5.com/es/users/nique_372"
|
|
#property strict
|
|
|
|
#ifndef EXPRESSEVALBYLEO_SRC_MATHEVAL_TOKENIZER_MQH
|
|
#define EXPRESSEVALBYLEO_SRC_MATHEVAL_TOKENIZER_MQH
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#include "Defines.mqh"
|
|
|
|
// @op(10 + 20 + )
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
struct CMathExpTokenizer : public CTokenizerBase
|
|
{
|
|
private:
|
|
ENUM_BINARY_AST_MATH_ERR m_last_err;
|
|
|
|
public:
|
|
CMathExpTokenizer(void) : m_last_err(WRONG_VALUE) {}
|
|
// ~CMathExpTokenizer(void) {}
|
|
|
|
//---
|
|
bool Tokenize(const string& text, TokenMathExp& tokns[]);
|
|
|
|
//---
|
|
__forceinline ENUM_BINARY_AST_MATH_ERR LastError() const { return m_last_err; }
|
|
string FormatLastError(const string& text) const;
|
|
|
|
//---
|
|
static void PrintValues(const TokenMathExp& tokens[]);
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
bool CMathExpTokenizer::Tokenize(const string &text, TokenMathExp &tokns[])
|
|
{
|
|
//---
|
|
m_len = StringLen(text);
|
|
m_pos = 0;
|
|
// m_err_pos = 0;
|
|
m_last_err = WRONG_VALUE;
|
|
|
|
//---
|
|
int reserve_size = 16;
|
|
ArrayResize(tokns, reserve_size, reserve_size);
|
|
int current_size = 0;
|
|
|
|
//---
|
|
while(m_pos < m_len)
|
|
{
|
|
//--- Saltamos espacios
|
|
if(text[m_pos] < 33)
|
|
{
|
|
m_pos++;
|
|
continue;
|
|
}
|
|
|
|
//---
|
|
switch(text[m_pos])
|
|
{
|
|
//---
|
|
case '#':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_FUNCTION_CUSTOM_CALL;
|
|
if(!CGenericParser::ParseFunction(text, m_len, m_pos, (int&)m_last_err, m_err_pos, tokns[current_size]))
|
|
{
|
|
return false;
|
|
}
|
|
AST_OPS_CHECK_RESIZE
|
|
|
|
//---
|
|
break;
|
|
}
|
|
|
|
//---
|
|
case '$':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_VARIABLE;
|
|
CGenericParser::ParseVariable(text, m_len, m_pos, (int&)m_last_err, m_err_pos, tokns[current_size]);
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
//---
|
|
case '%':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_MATEMATICO | int(BINARY_NODE_OP_MODULO) << AST_LOGIC_EXTRA_TYPE_START_BIT;
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
|
|
//---
|
|
case '&':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_MATEMATICO | int(BINARY_NODE_OP_BIT_AND) << AST_LOGIC_EXTRA_TYPE_START_BIT;
|
|
AST_OPS_CHECK_RESIZE
|
|
//---
|
|
break;
|
|
}
|
|
|
|
|
|
//--- Inicio de paraenteisis
|
|
case '(':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_PARA_INI;
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
//--- Final de parentenisis
|
|
case ')':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_PARA_END;
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
|
|
//---
|
|
case '*':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_MATEMATICO | int(BINARY_NODE_OP_MUL) << AST_LOGIC_EXTRA_TYPE_START_BIT;
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
//---
|
|
case '+':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_MATEMATICO | int(BINARY_NODE_OP_SUMA) << AST_LOGIC_EXTRA_TYPE_START_BIT;
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
//---
|
|
case '-':
|
|
{
|
|
//---
|
|
if(m_pos + 1 >= m_len)
|
|
{
|
|
m_last_err = BINARY_AST_TOKENIZER_ERR_SIGNO_MENOS_EN_FINAL_LEN;
|
|
m_err_pos = m_pos - 1;
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
if(IsDigit(text[m_pos + 1])) // -X (X es un numero)
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_NUMBER;
|
|
if(!CGenericParser::ParseNumberNoHex(text, m_len, m_pos, (int&)m_last_err, m_err_pos, tokns[current_size], m_pos++))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// Ahora mismo estamos en - (siguien char el que le sigue)
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_MATEMATICO | int(BINARY_NODE_OP_RESTA) << AST_LOGIC_EXTRA_TYPE_START_BIT;
|
|
}
|
|
|
|
//---
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
//---
|
|
case '/':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_MATEMATICO | int(BINARY_NODE_OP_DIVICION) << AST_LOGIC_EXTRA_TYPE_START_BIT;
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
//---
|
|
case '<':
|
|
{
|
|
m_pos++;
|
|
if(m_pos >= m_len)
|
|
{
|
|
m_last_err = BINARY_AST_TOKENIZER_ERR_DEZ_IZQ_INVALIDO;
|
|
m_err_pos = m_pos - 1;
|
|
return false;
|
|
}
|
|
else
|
|
if(text[m_pos] != '<')
|
|
{
|
|
m_last_err = BINARY_AST_TOKENIZER_ERR_DEZ_IZQ_SE_ESPERABA_EL_SIG_FALTANTE;
|
|
m_err_pos = m_pos - 1;
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_MATEMATICO | int(BINARY_NODE_OP_BIT_DEZPLAZAMIENTO_IZQ) << AST_LOGIC_EXTRA_TYPE_START_BIT;
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
//---
|
|
case '>':
|
|
{
|
|
m_pos++;
|
|
if(m_pos >= m_len)
|
|
{
|
|
m_last_err = BINARY_AST_TOKENIZER_ERR_DEZ_DRC_INVALIDO;
|
|
m_err_pos = m_pos - 1;
|
|
return false;
|
|
}
|
|
else
|
|
if(text[m_pos] != '>')
|
|
{
|
|
m_last_err = BINARY_AST_TOKENIZER_ERR_DEZ_DRC_SE_ESPERABA_EL_SIG_FALTANTE;
|
|
m_err_pos = m_pos - 1;
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_MATEMATICO | int(BINARY_NODE_OP_BIT_DEZPLAZAMIENTO_DRC) << AST_LOGIC_EXTRA_TYPE_START_BIT;
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
//---
|
|
case '@':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_FUNCTION_BUIL_IN_CALL;
|
|
if(!CGenericParser::ParseFunction(text, m_len, m_pos, (int&)m_last_err, m_err_pos, tokns[current_size]))
|
|
{
|
|
return false;
|
|
}
|
|
AST_OPS_CHECK_RESIZE
|
|
|
|
break;
|
|
}
|
|
|
|
//---
|
|
case '^':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_MATEMATICO | int(BINARY_NODE_OP_BIT_XOR) << AST_LOGIC_EXTRA_TYPE_START_BIT;
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
//---
|
|
case '|':
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_OPERDOR_MATEMATICO | int(BINARY_NODE_OP_BIT_OR) << AST_LOGIC_EXTRA_TYPE_START_BIT;
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
|
|
//---
|
|
//---
|
|
default:
|
|
{
|
|
if(IsDigit(text[m_pos]))
|
|
{
|
|
tokns[current_size].type = AST_TOKEN_MATH_T_VALUE_NUMBER;
|
|
if(!CGenericParser::ParseNumber(text, m_len, m_pos, (int&)m_last_err, m_err_pos, tokns[current_size]))
|
|
return false;
|
|
|
|
//---
|
|
AST_OPS_CHECK_RESIZE
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
m_last_err = BINARY_AST_TOKENIZER_ERR_INVALID_CHAR;
|
|
m_err_pos = m_pos;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--- Siguiente
|
|
m_pos++;
|
|
}
|
|
|
|
//---
|
|
ArrayResize(tokns, current_size);
|
|
|
|
//---
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
string CMathExpTokenizer::FormatLastError(const string& text) const
|
|
{
|
|
//---
|
|
if(m_last_err == WRONG_VALUE)
|
|
{
|
|
return "Not errors found";
|
|
}
|
|
|
|
//---
|
|
const uint r_err = m_last_err & 0xFFFFFF;
|
|
const int type = m_last_err >> AST_LOGIC_ERR_TYPE_START_BIT;
|
|
|
|
//---
|
|
int line = 0, col = 0;
|
|
GetCharPosLocation(m_err_pos, text, col, line);
|
|
|
|
//---
|
|
string error_msg = "";
|
|
|
|
//---
|
|
if(type == AST_LOGIC_ERR_TYPE_EXTERNAL)
|
|
{
|
|
CGenericParser::FormatErr(r_err, error_msg);
|
|
}
|
|
else
|
|
{
|
|
switch(r_err)
|
|
{
|
|
case BINARY_AST_TOKENIZER_ERR_DEZ_IZQ_INVALIDO:
|
|
error_msg = "Invalid left shift operator '<', expected '<<'";
|
|
break;
|
|
|
|
case BINARY_AST_TOKENIZER_ERR_DEZ_IZQ_SE_ESPERABA_EL_SIG_FALTANTE:
|
|
error_msg = "Left shift operator missing second '<', expected '<<'";
|
|
break;
|
|
|
|
case BINARY_AST_TOKENIZER_ERR_DEZ_DRC_INVALIDO:
|
|
error_msg = "Invalid right shift operator '>', expected '>>'";
|
|
break;
|
|
|
|
case BINARY_AST_TOKENIZER_ERR_DEZ_DRC_SE_ESPERABA_EL_SIG_FALTANTE:
|
|
error_msg = "Right shift operator missing second '>', expected '>>'";
|
|
break;
|
|
|
|
case BINARY_AST_TOKENIZER_ERR_SIGNO_MENOS_EN_FINAL_LEN:
|
|
error_msg = "Minus sign '-' found at end of expression";
|
|
break;
|
|
|
|
case BINARY_AST_TOKENIZER_ERR_INVALID_CHAR:
|
|
error_msg = "Invalid character in expression";
|
|
break;
|
|
|
|
default:
|
|
error_msg = "Unknown error";
|
|
break;
|
|
}
|
|
}
|
|
|
|
//---
|
|
return error_msg + StringFormat(" [Line %d, Col %d]", line, col);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
static void CMathExpTokenizer::PrintValues(const TokenMathExp &tokens[])
|
|
{
|
|
const int t = ArraySize(tokens);
|
|
Print(" TYPE | VALUE");
|
|
for(int i = 0; i < t; i++)
|
|
{
|
|
const int v = tokens[i].type & 0xFFFF;
|
|
PrintFormat("%s | %s", EnumToString(ENUM_AST_TOKEN_MATH_TYPE(v)), tokens[i].vs);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
#endif // EXPRESSEVALBYLEO_SRC_MATHEVAL_TOKENIZER_MQH
|