1
0
Fork 0

DBG: new expression parser (should be far more stable, might affect issue #312 and #310)

This commit is contained in:
Mr. eXoDia 2015-06-08 00:42:43 +02:00
parent 4be05b5974
commit a0be994c5a
11 changed files with 638 additions and 630 deletions

View File

@ -27,19 +27,15 @@ x64).</P>
registers (of all sizes) can be used as variables.</P>
<P class=rvps3><U>memory locations</U>: You can read from a memory location by using one of the
following expressions:<BR>[addr]&nbsp;&nbsp;&nbsp; - read a
DWORD/QWORD, depending on the architecture.<BR>@addr&nbsp;&nbsp;&nbsp;&nbsp; - same as
above.<BR><EM>n</EM>:[addr]&nbsp;&nbsp;- read <EM>n</EM> bytes.<BR>
@<EM>n</EM>:addr&nbsp;&nbsp; - same as
above.<BR><STRONG>REMARKS</STRONG>:<BR>- <EM>n</EM> is the amount of bytes to
read, this can be anything smaller than 4 on x32 and smaller than 8 on x64 when
specified, otherwise there will be an error.<BR>- addr is directly interpreted
as a value, when you want to read [addr+1] you should use
brackets:<BR>
DWORD/QWORD, depending on the
architecture.<BR><EM>n</EM>:[addr]&nbsp;&nbsp;- read <EM>n</EM> bytes
from.<BR><STRONG>REMARKS</STRONG>:<BR>- <EM>n</EM> is the amount of bytes to read, this can be anything
smaller than 4 on x32 and smaller than 8 on x64 when specified, otherwise
@(addr+1), @addr+1 will read: [addr]+1.</P>
there will be an error.</P>
<P class=rvps3><U>flags</U>: Debug
flags (interpreted as integer) can be used as input. Flags are prefixed with a
'!' following the flag name. Valid flags are: !cf, !pf, !af, !zf, !sf, !tf, !if,
@ -75,7 +71,7 @@ used.</DIV><U> </U>
"oep" or "ep" the address of these will be returned
instead.<BR><BR><STRONG>Notice</STRONG>: Instead of the ':' delimiter you can
also use a '.' If you need to query module information such as
"[module]:imagebase" or "[module]":entry" you are adviced to
"[module]:imagebase" or "[module]":entry" you are advised to
use a '?' as delimiter instead ("[module]?entry"). The '?'&nbsp;does
checking for named exports later, so it will still work when there is an
export called "entry" in the module.</LI></OL>

View File

@ -15,6 +15,7 @@
#include <shlwapi.h>
#include <stdarg.h>
#include <vector>
#include <stack>
#include <map>
#include <algorithm>
#include <unordered_map>

View File

@ -268,6 +268,32 @@ static bool isvalidexpression(const char* expression)
return valfromstring(expression, &value);
}
/**
\brief Check if a character is a mathematical operator. Used to determine stuff like "a *= b"
\param ch The character to check.
\return true if the character is an operator, false otherwise.
*/
static bool mathisoperator(const char ch)
{
switch(ch)
{
case '*':
case '`':
case '/':
case '%':
case '+':
case '-':
case '<':
case '>':
case '&':
case '^':
case '|':
return true;
default:
return false;
}
}
/**
\brief Special formats a given command. Used as a little hack to support stuff like 'x++' and 'x=y'
\param [in,out] string String to format.
@ -300,7 +326,7 @@ static void specialformat(char* string)
sprintf(str, "%s%c1", found, op);
strcpy(found, str);
}
if(mathisoperator(*a) > 2) //x*=3 -> x=x*3
if(mathisoperator(*a)) //x*=3 -> x=x*3
{
char op = *a;
*a = 0;
@ -350,8 +376,6 @@ COMMAND* cmdfindmain(COMMAND* cmd_list, char* command)
specialformat(command);
cmd = cmdget(cmd_list, command);
}
if(!cmd or !cmd->cbCommand)
mathformat(command);
return cmd;
}

View File

@ -0,0 +1,469 @@
#include "expressionparser.h"
#include "value.h"
ExpressionParser::Token::Token(const String & data, const Type type)
{
_data = data;
_type = type;
}
const String ExpressionParser::Token::data() const
{
return _data;
}
const ExpressionParser::Token::Type ExpressionParser::Token::type() const
{
return _type;
}
const ExpressionParser::Token::Associativity ExpressionParser::Token::associativity() const
{
switch(_type)
{
case Type::OperatorUnarySub:
case Type::OperatorNot:
return Associativity::RightToLeft;
case Type::OperatorMul:
case Type::OperatorHiMul:
case Type::OperatorDiv:
case Type::OperatorMod:
case Type::OperatorAdd:
case Type::OperatorSub:
case Type::OperatorShl:
case Type::OperatorShr:
case Type::OperatorAnd:
case Type::OperatorXor:
case Type::OperatorOr:
return Associativity::LeftToRight;
default:
return Associativity::Unspecified;
}
}
const int ExpressionParser::Token::precedence() const
{
switch(_type)
{
case Type::OperatorUnarySub:
case Type::OperatorNot:
return 7;
case Type::OperatorMul:
case Type::OperatorHiMul:
case Type::OperatorDiv:
case Type::OperatorMod:
return 6;
case Type::OperatorAdd:
case Type::OperatorSub:
return 5;
case Type::OperatorShl:
case Type::OperatorShr:
return 4;
case Type::OperatorAnd:
return 3;
case Type::OperatorXor:
return 2;
case Type::OperatorOr:
return 1;
default:
return 0;
}
}
const bool ExpressionParser::Token::isOperator() const
{
return _type != Type::Data && _type != Type::OpenBracket && _type != Type::CloseBracket;
}
ExpressionParser::ExpressionParser(const String & expression)
{
tokenize(fixClosingBrackets(expression));
shuntingYard();
}
String ExpressionParser::fixClosingBrackets(const String & expression)
{
int open = 0;
int close = 0;
size_t len = expression.length();
for(size_t i = 0; i < len; i++)
{
if(expression[i] == '(')
open++;
else if(expression[i] == ')')
close++;
}
String result = expression;
if(close < open)
{
for(int i = 0; i < open - close; i++)
result += ")";
}
return result;
}
void ExpressionParser::tokenize(const String & expression)
{
bool stateMemory = false;
size_t len = expression.length();
for(size_t i = 0; i < len; i++)
{
char ch = expression[i];
switch(ch)
{
case '[':
{
stateMemory = true;
_curToken += ch;
}
break;
case ']':
{
stateMemory = false;
_curToken += ch;
}
break;
default:
{
if(stateMemory)
_curToken += ch;
else
{
switch(ch)
{
case '(':
addOperatorToken(ch, Token::Type::OpenBracket);
break;
case ')':
addOperatorToken(ch, Token::Type::CloseBracket);
break;
case '~':
addOperatorToken(ch, Token::Type::OperatorNot);
break;
case '*':
addOperatorToken(ch, Token::Type::OperatorMul);
break;
case '`':
addOperatorToken(ch, Token::Type::OperatorHiMul);
break;
case '/':
addOperatorToken(ch, Token::Type::OperatorDiv);
break;
case '%':
addOperatorToken(ch, Token::Type::OperatorMod);
break;
case '+':
if(!isUnaryOperator()) //skip all unary add operators
addOperatorToken(ch, Token::Type::OperatorAdd);
break;
case '-':
if(isUnaryOperator())
addOperatorToken(ch, Token::Type::OperatorUnarySub);
else
addOperatorToken(ch, Token::Type::OperatorSub);
break;
case '<':
addOperatorToken(ch, Token::Type::OperatorShl);
break;
case '>':
addOperatorToken(ch, Token::Type::OperatorShr);
break;
case '&':
addOperatorToken(ch, Token::Type::OperatorAnd);
break;
case '^':
addOperatorToken(ch, Token::Type::OperatorXor);
break;
case '|':
addOperatorToken(ch, Token::Type::OperatorOr);
break;
case ' ': //ignore spaces
break;
default:
_curToken += ch;
break;
}
}
}
break;
}
}
if(_curToken.length() != 0) //make sure the last token is added
_tokens.push_back(Token(_curToken, Token::Type::Data));
}
void ExpressionParser::addOperatorToken(const char ch, const Token::Type type)
{
if(_curToken.length()) //add a new data token when there is data in the buffer
{
_tokens.push_back(Token(_curToken, Token::Type::Data));
_curToken = "";
}
String data;
data += ch;
_tokens.push_back(Token(data, type)); //add the operator token
}
bool ExpressionParser::isUnaryOperator()
{
if(_curToken.length()) //data before the operator means it is no unary operator
return false;
if(!_tokens.size()) //no tokens before the operator means it is an unary operator
return true;
Token lastToken = _tokens[_tokens.size() - 1];
return lastToken.isOperator(); //if the previous operator is a token, the operator is an unary operator
}
void ExpressionParser::shuntingYard()
{
//Implementation of Dijkstra's Shunting-yard algorithm
std::vector<Token> queue;
std::stack<Token> stack;
size_t len = _tokens.size();
//process the tokens
for(size_t i = 0; i < len; i++)
{
Token & token = _tokens[i];
switch(token.type())
{
case Token::Type::Data:
queue.push_back(token);
break;
case Token::Type::OpenBracket:
stack.push(token);
break;
case Token::Type::CloseBracket:
while(true)
{
if(stack.empty()) //empty stack = bracket mismatch
return;
Token curToken = stack.top();
stack.pop();
if(curToken.type() == Token::Type::OpenBracket)
break;
queue.push_back(curToken);
}
break;
default: //operator
Token & o1 = token;
while(!stack.empty())
{
Token o2 = stack.top();
if(o2.isOperator() &&
(o1.associativity() == Token::Associativity::LeftToRight && o1.precedence() <= o2.precedence()) ||
(o1.associativity() == Token::Associativity::RightToLeft && o1.precedence() < o2.precedence()))
{
queue.push_back(o2);
stack.pop();
}
else
break;
}
stack.push(o1);
break;
}
}
//pop the remaining operators
while(!stack.empty())
{
Token curToken = stack.top();
stack.pop();
if(curToken.type() == Token::Type::OpenBracket || curToken.type() == Token::Type::CloseBracket) //brackets on the stack means invalid expression
return;
queue.push_back(curToken);
}
_prefixTokens = queue;
}
#ifdef _WIN64
#include <intrin.h>
static inline unsigned long long umulhi(unsigned long long x, unsigned long long y)
{
unsigned __int64 res;
_umul128(x, y, &res);
return res;
}
static inline long long mulhi(long long x, long long y)
{
__int64 res;
_mul128(x, y, &res);
return res;
}
#else
static inline unsigned int umulhi(unsigned int x, unsigned int y)
{
return (unsigned int)(((unsigned long long)x * y) >> 32);
}
static inline int mulhi(int x, int y)
{
return (int)(((long long)x * y) >> 32);
}
#endif //__MINGW64__
template<typename T>
static bool operation(const ExpressionParser::Token::Type type, const T op1, const T op2, T & result)
{
result = 0;
switch(type)
{
case ExpressionParser::Token::Type::OperatorUnarySub:
result = op1 * ~0;
return true;
case ExpressionParser::Token::Type::OperatorNot:
result = ~op1;
return true;
case ExpressionParser::Token::Type::OperatorMul:
result = op1 * op2;
return true;
case ExpressionParser::Token::Type::OperatorHiMul:
result = umulhi(op1, op2);
return true;
case ExpressionParser::Token::Type::OperatorDiv:
if(op2 != 0)
{
result = op1 / op2;
return true;
}
return false;
case ExpressionParser::Token::Type::OperatorMod:
if(op2 != 0)
{
result = op1 % op2;
return true;
}
return false;
case ExpressionParser::Token::Type::OperatorAdd:
result = op1 + op2;
return true;
case ExpressionParser::Token::Type::OperatorSub:
result = op1 - op2;
return true;
case ExpressionParser::Token::Type::OperatorShl:
result = op1 << op2;
return true;
case ExpressionParser::Token::Type::OperatorShr:
result = op1 >> op2;
return true;
case ExpressionParser::Token::Type::OperatorAnd:
result = op1 & op2;
return true;
case ExpressionParser::Token::Type::OperatorXor:
result = op1 ^ op2;
return true;
case ExpressionParser::Token::Type::OperatorOr:
result = op1 | op2;
return true;
default:
return false;
}
}
bool ExpressionParser::unsignedoperation(const Token::Type type, const uint op1, const uint op2, uint & result)
{
if(type == Token::Type::OperatorHiMul)
{
result = umulhi(op1, op2);
return true;
}
else
return operation<uint>(type, op1, op2, result);
}
bool ExpressionParser::signedoperation(const Token::Type type, const sint op1, const sint op2, uint & result)
{
if(type == Token::Type::OperatorHiMul)
{
result = mulhi(op1, op2);
return true;
}
else
{
sint signedResult;
if(!operation<sint>(type, op1, op2, signedResult))
return false;
result = (uint)signedResult;
return true;
}
}
bool ExpressionParser::valFromString(const String & data, uint & value)
{
return sscanf_s(data.c_str(), "%u", &value) == 1;
}
bool ExpressionParser::calculate(uint & value, bool signedcalc, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly)
{
value = 0;
if(!_prefixTokens.size())
return false;
std::stack<uint> stack;
size_t len = _prefixTokens.size();
//calculate the result from the RPN queue
for(size_t i = 0; i < len; i++)
{
Token & token = _prefixTokens[i];
if(token.isOperator())
{
uint op1 = 0;
uint op2 = 0;
uint result = 0;
switch(token.type())
{
case Token::Type::OperatorUnarySub:
case Token::Type::OperatorNot:
if(stack.size() < 1)
return false;
op1 = stack.top();
stack.pop();
if(signedcalc)
signedoperation(token.type(), op1, op2, result);
else
unsignedoperation(token.type(), op1, op2, result);
stack.push(result);
break;
case Token::Type::OperatorMul:
case Token::Type::OperatorHiMul:
case Token::Type::OperatorDiv:
case Token::Type::OperatorMod:
case Token::Type::OperatorAdd:
case Token::Type::OperatorSub:
case Token::Type::OperatorShl:
case Token::Type::OperatorShr:
case Token::Type::OperatorAnd:
case Token::Type::OperatorXor:
case Token::Type::OperatorOr:
if(stack.size() < 2)
return false;
op2 = stack.top();
stack.pop();
op1 = stack.top();
stack.pop();
if(signedcalc)
signedoperation(token.type(), op1, op2, result);
else
unsignedoperation(token.type(), op1, op2, result);
stack.push(result);
break;
default: //do nothing
break;
}
}
else
{
uint result;
if(!valfromstring_noexpr(token.data().c_str(), &result, silent, baseonly, value_size, isvar, hexonly))
return false;
stack.push(result);
}
}
if(stack.empty()) //empty result stack means error
return false;
value = stack.top();
return true;
}

View File

@ -0,0 +1,69 @@
#ifndef _EXPRESSION_PARSER_H
#define _EXPRESSION_PARSER_H
#include "_global.h"
class ExpressionParser
{
public:
ExpressionParser(const String & expression);
bool calculate(uint & value, bool signedcalc, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly);
class Token
{
public:
enum class Type
{
Data,
OpenBracket,
CloseBracket,
OperatorUnarySub,
OperatorNot,
OperatorMul,
OperatorHiMul,
OperatorDiv,
OperatorMod,
OperatorAdd,
OperatorSub,
OperatorShl,
OperatorShr,
OperatorAnd,
OperatorXor,
OperatorOr
};
enum class Associativity
{
LeftToRight,
RightToLeft,
Unspecified
};
Token(const String & data, const Type type);
const String data() const;
const Type type() const;
const Associativity associativity() const;
const int precedence() const;
const bool isOperator() const;
private:
String _data;
Type _type;
};
private:
String fixClosingBrackets(const String & expression);
bool isUnaryOperator();
void tokenize(const String & expression);
void shuntingYard();
void addOperatorToken(const char ch, const Token::Type type);
bool unsignedoperation(const Token::Type type, const uint op1, const uint op2, uint & result);
bool signedoperation(const Token::Type type, const sint op1, const sint op2, uint & result);
bool valFromString(const String & data, uint & value);
std::vector<Token> _tokens;
std::vector<Token> _prefixTokens;
String _curToken;
};
#endif //_EXPRESSION_PARSER_H

View File

@ -1,520 +0,0 @@
/**
@file math.cpp
@brief Implements various functionalities that have to do with handling expression text.
*/
#include "math.h"
#include "value.h"
/**
\brief A bracket pair. This structure describes a piece of brackets '(' and ')'
*/
struct BRACKET_PAIR
{
/**
\brief The position in the string of the opening bracket '('.
*/
int openpos;
/**
\brief The position in the string of the closing bracket ')'.
*/
int closepos;
/**
\brief The depth of the pair (for example when you have "((1+4)*4)" the second '(' has layer 2).
*/
int layer;
/**
\brief 0 when there is nothing set, 1 when the openpos is set, 2 when everything is set (aka pair is complete).
*/
int isset;
};
/**
\brief An expression. This structure describes an expression in form of bracket pairs.
*/
struct EXPRESSION
{
/**
\brief The bracket pairs.
*/
BRACKET_PAIR* pairs;
/**
\brief The total number of bracket pairs.
*/
int total_pairs;
/**
\brief The expression text everything is derived from.
*/
const char* expression;
};
/**
\brief Determines if a characters is an operator.
\param ch The character to check.
\return The number of the operator. 0 when the character is no operator. Otherwise it returns one of the following numbers:
- 1 ( )
- 2 ~ (NOT)
- 3 * / % (MUL DIV)
- 4 + - (ADD SUB)
- 5 < > (SHL SHR)
- 6 & (AND)
- 7 ^ (XOR)
- 8 | (OR)
*/
int mathisoperator(char ch)
{
if(ch == '(' or ch == ')')
return 1;
else if(ch == '~')
return 2;
else if(ch == '*' or ch == '`' or ch == '/' or ch == '%')
return 3;
else if(ch == '+' or ch == '-')
return 4;
else if(ch == '<' or ch == '>')
return 5;
else if(ch == '&')
return 6;
else if(ch == '^')
return 7;
else if(ch == '|')
return 8;
return 0;
}
/**
\brief Formats the given text. It removes double operators like "**" and "||"
\param [in,out] text The text to format.
*/
void mathformat(char* text)
{
int len = (int)strlen(text);
Memory<char*> temp(len + 1, "mathformat:temp");
memset(temp, 0, len + 1);
for(int i = 0, j = 0; i < len; i++)
if(mathisoperator(text[i]) < 3 or text[i] != text[i + 1])
j += sprintf(temp + j, "%c", text[i]);
strcpy(text, temp);
}
/**
\brief Checks if the given text contains operators.
\param text The text to check.
\return true if the text contains operator, false if not.
*/
bool mathcontains(const char* text)
{
if(*text == '-') //ignore negative values
text++;
int len = (int)strlen(text);
for(int i = 0; i < len; i++)
if(mathisoperator(text[i]))
return true;
return false;
}
#ifdef __MINGW64__
static inline unsigned long long umulhi(unsigned long long x, unsigned long long y)
{
return (unsigned long long)(((__uint128_t)x * y) >> 64);
}
static inline long long mulhi(long long x, long long y)
{
return (long long)(((__int128_t)x * y) >> 64);
}
#elif _WIN64
#include <intrin.h>
static inline unsigned long long umulhi(unsigned long long x, unsigned long long y)
{
unsigned __int64 res;
_umul128(x, y, &res);
return res;
}
static inline long long mulhi(long long x, long long y)
{
__int64 res;
_mul128(x, y, &res);
return res;
}
#else
static inline unsigned int umulhi(unsigned int x, unsigned int y)
{
return (unsigned int)(((unsigned long long)x * y) >> 32);
}
static inline int mulhi(int x, int y)
{
return (int)(((long long)x * y) >> 32);
}
#endif //__MINGW64__
/**
\brief Do an operation on two unsigned numbers.
\param op The operation to do (this must be a valid operator).
\param left The number left of the operator.
\param right The number right of the operator.
\param [out] result The result of the operator. Cannot be null.
\return true if the operation succeeded. It could fail on zero devision or an invalid operator.
*/
bool mathdounsignedoperation(char op, uint left, uint right, uint* result)
{
switch(op)
{
case '*':
*result = left * right;
return true;
case '`':
*result = umulhi(left, right);
return true;
case '/':
if(right)
{
*result = left / right;
return true;
}
return false;
case '%':
if(right)
{
*result = left % right;
return true;
}
return false;
case '+':
*result = left + right;
return true;
case '-':
*result = left - right;
return true;
case '<':
*result = left << right;
return true;
case '>':
*result = left >> right;
return true;
case '&':
*result = left & right;
return true;
case '^':
*result = left ^ right;
return true;
case '|':
*result = left | right;
return true;
}
return false;
}
/**
\brief Do an operation on two signed numbers.
\param op The operation to do (this must be a valid operator).
\param left The number left of the operator.
\param right The number right of the operator.
\param [out] result The result of the operator. Cannot be null.
\return true if the operation succeeded. It could fail on zero devision or an invalid operator.
*/
bool mathdosignedoperation(char op, sint left, sint right, sint* result)
{
switch(op)
{
case '*':
*result = left * right;
return true;
case '`':
*result = mulhi(left, right);
return true;
case '/':
if(right)
{
*result = left / right;
return true;
}
return false;
case '%':
if(right)
{
*result = left % right;
return true;
}
return false;
case '+':
*result = left + right;
return true;
case '-':
*result = left - right;
return true;
case '<':
*result = left << right;
return true;
case '>':
*result = left >> right;
return true;
case '&':
*result = left & right;
return true;
case '^':
*result = left ^ right;
return true;
case '|':
*result = left | right;
return true;
}
return false;
}
/**
\brief Fills a BRACKET_PAIR structure.
\param [in,out] expstruct The expression structure. Cannot be null.
\param pos The position to fill, position of '(' or ')'.
\param layer The layer this bracket is in.
*/
static void fillpair(EXPRESSION* expstruct, int pos, int layer)
{
for(int i = 0; i < expstruct->total_pairs; i++)
{
if(!expstruct->pairs[i].isset)
{
expstruct->pairs[i].layer = layer;
expstruct->pairs[i].openpos = pos;
expstruct->pairs[i].isset = 1;
break;
}
else if(expstruct->pairs[i].layer == layer and expstruct->pairs[i].isset == 1)
{
expstruct->pairs[i].closepos = pos;
expstruct->pairs[i].isset = 2;
break;
}
}
}
/**
\brief This function recursively matches bracket pair in an EXPRESSION.
\param [in,out] expstruct The expression structure. Cannot be null.
\param expression The expression text to parse. Cannot be null.
\param endlayer The layer to stop on. This variable is used for the recursion termination condition.
\return The position in the \p expression mathpairs ended in.
*/
static int matchpairs(EXPRESSION* expstruct, const char* expression, int endlayer = 0)
{
int layer = endlayer;
int len = (int)strlen(expression);
for(int i = 0; i < len; i++)
{
if(expression[i] == '(')
{
layer++;
int pos = (int)(expression + i - expstruct->expression);
fillpair(expstruct, pos, layer);
i += matchpairs(expstruct, expression + i + 1, layer);
}
else if(expression[i] == ')')
{
if(layer == endlayer)
{
int pos = (int)(expression + i - expstruct->expression);
fillpair(expstruct, pos, layer);
return i;
}
layer--;
}
}
return 0;
}
/**
\brief Formats a given expression. This function checks if the number of brackets is even and adds brackets to the end if needed.
\param [in,out] exp The expression to format.
\return The number of bracket pairs in the expression or -1 on error.
*/
static int expressionformat(char* exp, size_t bufsize)
{
int len = (int)strlen(exp);
int open = 0;
int close = 0;
for(int i = 0; i < len; i++)
{
if(exp[i] == '(')
open++;
else if(exp[i] == ')')
close++;
}
if(close > open)
return -1;
int add = open - close;
if(add)
{
String closing;
for(int i = 0; i < add; i++)
closing += ')';
strcat_s(exp, bufsize, closing.c_str());
}
return open;
}
/**
\brief Adjusts bracket pair positions to insert a new string in the expression.
\param [in,out] exps The expression structure.
\param cur_open The current opening bracket '(' position.
\param cur_close The current closing bracket ')' position.
\param cur_len The current string length in between the brackets.
\param new_len Length of the new string.
*/
static void adjustpairs(EXPRESSION* exps, int cur_open, int cur_close, int cur_len, int new_len)
{
for(int i = 0; i < exps->total_pairs; i++)
{
if(exps->pairs[i].openpos > cur_open)
exps->pairs[i].openpos += new_len - cur_len;
if(exps->pairs[i].closepos > cur_close)
exps->pairs[i].closepos += new_len - cur_len;
}
}
/**
\brief Prints value of expressions in between brackets on a certain bracket layer (expression is resolved using mathfromstring(), which means the whole thing can work recursively).
\param [in,out] exp The expression to print. Cannot be null.
\param bufsize Buffer size of the expression parameter.
\param [in,out] exps The expression structure. Cannot be null.
\param layer The layer to print.
\param silent Value to pass on to mathfromstring().
\param baseonly Value to pass on to mathfromstring().
\return true if printing the layer was successful, false otherwise.
*/
static bool printlayer(char* exp, size_t bufsize, EXPRESSION* exps, int layer, bool silent, bool baseonly)
{
for(int i = 0; i < exps->total_pairs; i++)
{
if(exps->pairs[i].layer == layer)
{
const size_t explen = strlen(exp);
Memory<char*> temp(explen + 1);
Memory<char*> backup(explen + 1);
int open = exps->pairs[i].openpos;
int close = exps->pairs[i].closepos;
int len = close - open;
strncpy_s(temp, temp.size(), exp + open + 1, len - 1);
strcpy_s(backup, backup.size(), exp + open + len + 1);
uint value;
if(!mathfromstring(temp, &value, silent, baseonly, 0, 0))
return false;
adjustpairs(exps, open, close, len + 1, sprintf_s(exp + open, bufsize - open, "%"fext"X", value));
if(*backup)
strcat_s(exp, bufsize, backup);
}
}
return true;
}
/**
\brief Handle brackets in an expression (calculate the values of expressions in between brackets).
\param [in,out] expression Expression to handle. Cannot be null. You need at most 16 times the input size for this.
\param bufsize Buffer size of the expression parameter.
\param silent Value to pass on to printlayer().
\param baseonly Value to pass on to printlayer().
\return true if the brackets are correctly expanded, false otherwise.
*/
bool mathhandlebrackets(char* expression, size_t bufsize, bool silent, bool baseonly)
{
EXPRESSION expstruct;
expstruct.expression = expression;
int total_pairs = expressionformat(expression, bufsize);
if(total_pairs == -1)
return false;
else if(!total_pairs)
return true;
expstruct.total_pairs = total_pairs;
Memory<BRACKET_PAIR*> pairs(expstruct.total_pairs * sizeof(BRACKET_PAIR), "mathhandlebrackets:expstruct.pairs");
expstruct.pairs = pairs;
memset(expstruct.pairs, 0, expstruct.total_pairs * sizeof(BRACKET_PAIR));
matchpairs(&expstruct, expression);
int deepest = 0;
for(int i = 0; i < expstruct.total_pairs; i++)
if(expstruct.pairs[i].layer > deepest)
deepest = expstruct.pairs[i].layer;
for(int i = deepest; i > 0; i--)
if(!printlayer(expression, bufsize, &expstruct, i, silent, baseonly))
return false;
return true;
}
/**
\brief Calculate the value of an expression string.
\param string The string to calculate the value of. Cannot be null.
\param [in,out] value The resulting value. Cannot be null.
\param silent Value to pass on to valfromstring().
\param baseonly Value to pass on to valfromstring().
\param [in,out] value_size Value to pass on to valfromstring(). Can be null.
\param [in,out] isvar Value to pass on to valfromstring(). Can be null.
\return true if the string was successfully parsed and the value was calculated.
*/
bool mathfromstring(const char* string, uint* value, bool silent, bool baseonly, int* value_size, bool* isvar)
{
int highestop = 0;
int highestop_pos = 0;
int len = (int)strlen(string);
bool negative = false;
if(*string == '-')
{
negative = true;
string++;
}
for(int i = 0; i < len; i++)
{
int curop = mathisoperator(string[i]);
if(curop > 1 and curop > highestop)
{
highestop = curop;
highestop_pos = i;
}
}
if(!highestop)
return valfromstring(string, value, silent, baseonly, value_size, isvar, 0);
Memory<char*> strleft(len + 1 + negative, "mathfromstring:strleft");
Memory<char*> strright(len + 1, "mathfromstring:strright");
strncpy(strleft, string - negative, highestop_pos + negative);
strcpy(strright, string + highestop_pos + 1);
strcpy(strleft, StringUtils::Trim(strleft).c_str());
strcpy(strright, StringUtils::Trim(strright).c_str());
//dprintf("left: %s, right: %s, op: %c\n", strleft(), strright(), string[highestop_pos]);
if(!*strright)
return false;
uint right = 0;
if(!valfromstring(strright, &right, silent, baseonly))
return false;
if(string[highestop_pos] == '~')
{
right = ~right;
if(!strlen(strleft))
{
*value = right;
return true;
}
}
uint left = 0;
if(!valfromstring(strleft, &left, silent, baseonly))
return false;
bool math_ok;
if(valuesignedcalc())
math_ok = mathdosignedoperation(string[highestop_pos], left, right, (sint*)value);
else
math_ok = mathdounsignedoperation(string[highestop_pos], left, right, value);
return math_ok;
}

View File

@ -1,14 +0,0 @@
#ifndef _MATH_H
#define _MATH_H
#include "_global.h"
int mathisoperator(char ch);
void mathformat(char* text);
bool mathcontains(const char* text);
bool mathhandlebrackets(char* expression, size_t bufsize, bool silent, bool baseonly);
bool mathfromstring(const char* string, uint* value, bool silent, bool baseonly, int* value_size, bool* isvar);
bool mathdounsignedoperation(char op, uint left, uint right, uint* result);
bool mathdosignedoperation(char op, sint left, sint right, sint* result);
#endif // _MATH_H

View File

@ -14,6 +14,7 @@
#include "symbolinfo.h"
#include "module.h"
#include "label.h"
#include "expressionparser.h"
static bool dosignedcalc = false;
@ -1497,9 +1498,9 @@ static bool ishexnumber(const char* string)
\param [out] value_size This function can output the value size parsed (for example memory location size or register size). Can be null.
\param [out] isvar This function can output if the expression is variable (for example memory locations, registers or variables are variable). Can be null.
\param [out] hexonly This function can output if the output value should only be printed as hexadecimal (for example addresses). Can be null.
\return true if the expression was parsed successfull, false otherwise.
\return true if the expression was parsed successful, false otherwise.
*/
bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly)
bool valfromstring_noexpr(const char* string, uint* value, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly)
{
if(!value or !string)
return false;
@ -1508,50 +1509,7 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly,
*value = 0;
return true;
}
else if(mathcontains(string)) //handle math
{
int len = (int)strlen(string);
Memory<char*> newstring(len * 2 + 1, "valfromstring:newstring");
if(strstr(string, "[")) //memory brackets: []
{
for(int i = 0, j = 0; i < len; i++)
{
if(string[i] == ']')
j += sprintf(newstring + j, ")");
else if(isdigit(string[i]) and string[i + 1] == ':' and string[i + 2] == '[') //n:[
{
j += sprintf(newstring + j, "@%c:(", string[i]);
i += 2;
}
else if(string[i] == '[')
j += sprintf(newstring + j, "@(");
else
j += sprintf(newstring + j, "%c", string[i]);
}
}
else
strcpy_s(newstring, newstring.size(), string);
Memory<char*> string_(len * 16 + deflen, "valfromstring:string_");
strcpy_s(string_, string_.size(), newstring);
int add = 0;
bool negative = (*string_ == '-');
while(mathisoperator(string_[add + negative]) > 2)
add++;
if(!mathhandlebrackets(string_ + add, string_.size() - add, silent, baseonly))
return false;
return mathfromstring(string_ + add, value, silent, baseonly, value_size, isvar);
}
else if(*string == '-') //negative value
{
uint val;
if(!valfromstring(string + 1, &val, silent, baseonly, value_size, isvar, hexonly))
return false;
val *= ~0;
if(value)
*value = val;
return true;
}
else if(*string == '@' or strstr(string, "[")) //memory location
else if(string[0] == '[' || (isdigit(string[0]) && string[1] == ':' && string[2] == '[')) //memory location
{
if(!DbgIsDebugging())
{
@ -1565,37 +1523,33 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly,
return true;
}
int len = (int)strlen(string);
Memory<char*> newstring(len * 2 + 1, "valfromstring:newstring");
if(strstr(string, "["))
String newstring;
bool foundStart = false;
for(int i = 0; i < len; i++)
{
for(int i = 0, j = 0; i < len; i++)
{
if(string[i] == ']')
j += sprintf(newstring + j, ")");
else if(isdigit(string[i]) and string[i + 1] == ':' and string[i + 2] == '[') //n:[
{
j += sprintf(newstring + j, "@%c:(", string[i]);
i += 2;
}
else if(string[i] == '[')
j += sprintf(newstring + j, "@(");
else
j += sprintf(newstring + j, "%c", string[i]);
}
if(string[i] == '[')
foundStart = true;
else if(string[i] == ']')
break;
else if(foundStart)
newstring += string[i];
}
else
strcpy_s(newstring, newstring.size(), string);
int read_size = sizeof(uint);
int add = 1;
if(newstring[2] == ':' and isdigit((newstring[1]))) //@n: (number of bytes to read)
if(string[1] == ':') //n:[ (number of bytes to read)
{
add += 2;
int new_size = newstring[1] - 0x30;
int new_size = string[0] - '0';
if(new_size < read_size)
read_size = new_size;
}
if(!valfromstring(newstring + add, value, silent, baseonly))
if(!valfromstring(newstring.c_str(), value, silent, baseonly))
{
dprintf("noexpr failed on %s\n", newstring.c_str());
return false;
}
uint addr = *value;
*value = 0;
if(!MemRead((void*)addr, value, read_size, 0))
@ -1610,7 +1564,7 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly,
*isvar = true;
return true;
}
else if(isregister(string)) //register
else if(isregister(string)) //register
{
if(!DbgIsDebugging())
{
@ -1628,7 +1582,7 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly,
*isvar = true;
return true;
}
else if(*string == '!' and isflag(string + 1)) //flag
else if(*string == '!' and isflag(string + 1)) //flag
{
if(!DbgIsDebugging())
{
@ -1652,7 +1606,7 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly,
*isvar = true;
return true;
}
else if(isdecnumber(string)) //decimal numbers come 'first'
else if(isdecnumber(string)) //decimal numbers come 'first'
{
if(value_size)
*value_size = 0;
@ -1661,7 +1615,7 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly,
sscanf(string + 1, "%"fext"u", value);
return true;
}
else if(ishexnumber(string)) //then hex numbers
else if(ishexnumber(string)) //then hex numbers
{
if(value_size)
*value_size = 0;
@ -1676,13 +1630,13 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly,
}
if(baseonly)
return false;
else if(valapifromstring(string, value, value_size, true, silent, hexonly)) //then come APIs
else if(valapifromstring(string, value, value_size, true, silent, hexonly)) //then come APIs
return true;
else if(LabelFromString(string, value)) //then come labels
else if(LabelFromString(string, value)) //then come labels
return true;
else if(SymAddrFromName(string, value)) //then come symbols
else if(SymAddrFromName(string, value)) //then come symbols
return true;
else if(varget(string, value, value_size, 0)) //finally variables
else if(varget(string, value, value_size, 0)) //finally variables
{
if(isvar)
*isvar = true;
@ -1693,6 +1647,34 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly,
return false; //nothing was OK
}
/**
\brief Gets a value from a string. This function can parse expressions, memory locations, registers, flags, API names, labels, symbols and variables.
\param string The string to parse.
\param [out] value The value of the expression. This value cannot be null.
\param silent true to not output anything to the console.
\param baseonly true to skip parsing API names, labels, symbols and variables (basic expressions only).
\param [out] value_size This function can output the value size parsed (for example memory location size or register size). Can be null.
\param [out] isvar This function can output if the expression is variable (for example memory locations, registers or variables are variable). Can be null.
\param [out] hexonly This function can output if the output value should only be printed as hexadecimal (for example addresses). Can be null.
\return true if the expression was parsed successful, false otherwise.
*/
bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly)
{
if(!value or !string)
return false;
if(!*string)
{
*value = 0;
return true;
}
ExpressionParser parser(string);
uint result;
if(!parser.calculate(result, valuesignedcalc(), silent, baseonly, value_size, isvar, hexonly))
return false;
*value = result;
return true;
}
/**
\brief Checks if a string is long enough.
\param str The string to check.

View File

@ -7,6 +7,7 @@
bool valuesignedcalc();
void valuesetsignedcalc(bool a);
bool valapifromstring(const char* name, uint* value, int* value_size, bool printall, bool silent, bool* hexonly);
bool valfromstring_noexpr(const char* string, uint* value, bool silent = true, bool baseonly = false, int* value_size = 0, bool* isvar = 0, bool* hexonly = 0);
bool valfromstring(const char* string, uint* value, bool silent = true, bool baseonly = false, int* value_size = 0, bool* isvar = 0, bool* hexonly = 0);
bool valflagfromstring(uint eflags, const char* string);
bool valtostring(const char* string, uint value, bool silent);

View File

@ -35,6 +35,7 @@
<ClCompile Include="disasm_helper.cpp" />
<ClCompile Include="error.cpp" />
<ClCompile Include="exception.cpp" />
<ClCompile Include="expressionparser.cpp" />
<ClCompile Include="filereader.cpp" />
<ClCompile Include="function.cpp" />
<ClCompile Include="functionanalysis.cpp" />
@ -42,7 +43,6 @@
<ClCompile Include="label.cpp" />
<ClCompile Include="loop.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="math.cpp" />
<ClCompile Include="memory.cpp" />
<ClCompile Include="module.cpp" />
<ClCompile Include="msgqueue.cpp" />
@ -96,6 +96,7 @@
<ClInclude Include="dynamicmem.h" />
<ClInclude Include="error.h" />
<ClInclude Include="exception.h" />
<ClInclude Include="expressionparser.h" />
<ClInclude Include="filereader.h" />
<ClInclude Include="function.h" />
<ClInclude Include="functionanalysis.h" />
@ -109,7 +110,6 @@
<ClInclude Include="lz4\lz4.h" />
<ClInclude Include="lz4\lz4file.h" />
<ClInclude Include="lz4\lz4hc.h" />
<ClInclude Include="math.h" />
<ClInclude Include="memory.h" />
<ClInclude Include="module.h" />
<ClInclude Include="msgqueue.h" />

View File

@ -105,9 +105,6 @@
<ClCompile Include="console.cpp">
<Filter>Source Files\Core</Filter>
</ClCompile>
<ClCompile Include="math.cpp">
<Filter>Source Files\Core</Filter>
</ClCompile>
<ClCompile Include="threading.cpp">
<Filter>Source Files\Core</Filter>
</ClCompile>
@ -219,6 +216,9 @@
<ClCompile Include="filereader.cpp">
<Filter>Source Files\Utilities</Filter>
</ClCompile>
<ClCompile Include="expressionparser.cpp">
<Filter>Source Files\Core</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="x64_dbg.h">
@ -260,9 +260,6 @@
<ClInclude Include="command.h">
<Filter>Header Files\Core</Filter>
</ClInclude>
<ClInclude Include="math.h">
<Filter>Header Files\Core</Filter>
</ClInclude>
<ClInclude Include="threading.h">
<Filter>Header Files\Core</Filter>
</ClInclude>
@ -518,5 +515,8 @@
<ClInclude Include="filereader.h">
<Filter>Header Files\Utilities</Filter>
</ClInclude>
<ClInclude Include="expressionparser.h">
<Filter>Header Files\Core</Filter>
</ClInclude>
</ItemGroup>
</Project>