1021 lines
38 KiB
C++
1021 lines
38 KiB
C++
#include "expressionparser.h"
|
|
#include "value.h"
|
|
#include "console.h"
|
|
#include "variable.h"
|
|
#include "expressionfunctions.h"
|
|
#include <algorithm>
|
|
|
|
ExpressionParser::Token::Associativity ExpressionParser::Token::associativity() const
|
|
{
|
|
switch(mType)
|
|
{
|
|
case Type::OperatorUnarySub:
|
|
case Type::OperatorUnaryAdd:
|
|
case Type::OperatorNot:
|
|
case Type::OperatorLogicalNot:
|
|
case Type::OperatorAssign:
|
|
case Type::OperatorAssignMul:
|
|
case Type::OperatorAssignHiMul:
|
|
case Type::OperatorAssignDiv:
|
|
case Type::OperatorAssignMod:
|
|
case Type::OperatorAssignAdd:
|
|
case Type::OperatorAssignSub:
|
|
case Type::OperatorAssignShl:
|
|
case Type::OperatorAssignShr:
|
|
case Type::OperatorAssignRol:
|
|
case Type::OperatorAssignRor:
|
|
case Type::OperatorAssignAnd:
|
|
case Type::OperatorAssignXor:
|
|
case Type::OperatorAssignOr:
|
|
case Type::OperatorPrefixInc:
|
|
case Type::OperatorPrefixDec:
|
|
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::OperatorRol:
|
|
case Type::OperatorRor:
|
|
case Type::OperatorAnd:
|
|
case Type::OperatorXor:
|
|
case Type::OperatorOr:
|
|
case Type::OperatorEqual:
|
|
case Type::OperatorNotEqual:
|
|
case Type::OperatorBigger:
|
|
case Type::OperatorSmaller:
|
|
case Type::OperatorBiggerEqual:
|
|
case Type::OperatorSmallerEqual:
|
|
case Type::OperatorLogicalAnd:
|
|
case Type::OperatorLogicalOr:
|
|
case Type::OperatorLogicalImpl:
|
|
case Type::OperatorSuffixInc:
|
|
case Type::OperatorSuffixDec:
|
|
return Associativity::LeftToRight;
|
|
default:
|
|
return Associativity::Unspecified;
|
|
}
|
|
}
|
|
|
|
//As defined in http://en.cppreference.com/w/c/language/operator_precedence
|
|
|
|
int ExpressionParser::Token::precedence() const
|
|
{
|
|
switch(mType)
|
|
{
|
|
case Type::OperatorSuffixInc:
|
|
case Type::OperatorSuffixDec:
|
|
return 1;
|
|
case Type::OperatorUnarySub:
|
|
case Type::OperatorUnaryAdd:
|
|
case Type::OperatorNot:
|
|
case Type::OperatorLogicalNot:
|
|
case Type::OperatorPrefixInc:
|
|
case Type::OperatorPrefixDec:
|
|
return 2;
|
|
case Type::OperatorMul:
|
|
case Type::OperatorHiMul:
|
|
case Type::OperatorDiv:
|
|
case Type::OperatorMod:
|
|
return 3;
|
|
case Type::OperatorAdd:
|
|
case Type::OperatorSub:
|
|
return 4;
|
|
case Type::OperatorShl:
|
|
case Type::OperatorShr:
|
|
case Type::OperatorRol:
|
|
case Type::OperatorRor:
|
|
return 5;
|
|
case Type::OperatorSmaller:
|
|
case Type::OperatorSmallerEqual:
|
|
case Type::OperatorBigger:
|
|
case Type::OperatorBiggerEqual:
|
|
return 6;
|
|
case Type::OperatorEqual:
|
|
case Type::OperatorNotEqual:
|
|
return 7;
|
|
case Type::OperatorAnd:
|
|
return 8;
|
|
case Type::OperatorXor:
|
|
return 9;
|
|
case Type::OperatorOr:
|
|
return 10;
|
|
case Type::OperatorLogicalAnd:
|
|
return 11;
|
|
case Type::OperatorLogicalOr:
|
|
case Type::OperatorLogicalImpl:
|
|
return 12;
|
|
case Type::OperatorAssign:
|
|
case Type::OperatorAssignMul:
|
|
case Type::OperatorAssignHiMul:
|
|
case Type::OperatorAssignDiv:
|
|
case Type::OperatorAssignMod:
|
|
case Type::OperatorAssignAdd:
|
|
case Type::OperatorAssignSub:
|
|
case Type::OperatorAssignShl:
|
|
case Type::OperatorAssignShr:
|
|
case Type::OperatorAssignRol:
|
|
case Type::OperatorAssignRor:
|
|
case Type::OperatorAssignAnd:
|
|
case Type::OperatorAssignXor:
|
|
case Type::OperatorAssignOr:
|
|
return 14;
|
|
default:
|
|
return 16;
|
|
}
|
|
}
|
|
|
|
bool ExpressionParser::Token::isOperator() const
|
|
{
|
|
return mType >= Type::OperatorUnarySub;
|
|
}
|
|
|
|
ExpressionParser::ExpressionParser(const String & expression)
|
|
: mExpression(fixClosingBrackets(expression)),
|
|
mIsValidExpression(true)
|
|
{
|
|
const size_t r = 50;
|
|
mTokens.reserve(r);
|
|
mCurToken.reserve(r);
|
|
tokenize();
|
|
shuntingYard();
|
|
}
|
|
|
|
String ExpressionParser::fixClosingBrackets(const String & expression)
|
|
{
|
|
size_t open = 0;
|
|
size_t close = 0;
|
|
auto len = expression.length();
|
|
for(size_t i = 0; i < len; i++)
|
|
{
|
|
if(expression[i] == '(')
|
|
open++;
|
|
else if(expression[i] == ')')
|
|
close++;
|
|
}
|
|
auto result = expression;
|
|
if(close < open)
|
|
{
|
|
for(size_t i = 0; i < open - close; i++)
|
|
result += ")";
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void ExpressionParser::tokenize()
|
|
{
|
|
size_t stateMemory = 0;
|
|
auto stateQuote = false;
|
|
auto len = mExpression.length();
|
|
auto push_token = [this, &stateQuote](char ch)
|
|
{
|
|
mCurToken.push_back(ch);
|
|
mCurTokenQuoted.push_back(stateQuote);
|
|
};
|
|
for(size_t i = 0; i < len; i++)
|
|
{
|
|
auto ch = mExpression[i];
|
|
switch(ch)
|
|
{
|
|
case '\"':
|
|
{
|
|
stateQuote = !stateQuote;
|
|
}
|
|
break;
|
|
|
|
case '[':
|
|
{
|
|
stateMemory++;
|
|
push_token(ch);
|
|
}
|
|
break;
|
|
|
|
case ']':
|
|
{
|
|
if(stateMemory)
|
|
stateMemory--;
|
|
else
|
|
mIsValidExpression = false;
|
|
push_token(ch);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
if(stateMemory || stateQuote)
|
|
push_token(ch);
|
|
else
|
|
{
|
|
switch(ch)
|
|
{
|
|
case ',':
|
|
addOperatorToken(ch, Token::Type::Comma);
|
|
break;
|
|
case '(':
|
|
addOperatorToken(ch, Token::Type::OpenBracket);
|
|
break;
|
|
case ')':
|
|
addOperatorToken(ch, Token::Type::CloseBracket);
|
|
break;
|
|
case '~':
|
|
addOperatorToken(ch, Token::Type::OperatorNot);
|
|
break;
|
|
case '*':
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken("*=", Token::Type::OperatorAssignMul);
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorMul);
|
|
break;
|
|
case '`':
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken("`=", Token::Type::OperatorAssignHiMul);
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorHiMul);
|
|
break;
|
|
case '/':
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken("/=", Token::Type::OperatorAssignDiv);
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorDiv);
|
|
break;
|
|
case '%':
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken("%=", Token::Type::OperatorAssignMod);
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorMod);
|
|
break;
|
|
case '+':
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken("+=", Token::Type::OperatorAssignAdd);
|
|
else if(tryEatNextCh(i, '+'))
|
|
addOperatorToken("++", isUnaryOperator() ? Token::Type::OperatorPrefixInc : Token::Type::OperatorSuffixInc);
|
|
else if(isUnaryOperator())
|
|
addOperatorToken(ch, Token::Type::OperatorUnaryAdd);
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorAdd);
|
|
break;
|
|
case '-':
|
|
if(tryEatNextCh(i, '>'))
|
|
addOperatorToken("->", Token::Type::OperatorLogicalImpl);
|
|
else if(tryEatNextCh(i, '='))
|
|
addOperatorToken("-=", Token::Type::OperatorAssignSub);
|
|
else if(tryEatNextCh(i, '-'))
|
|
addOperatorToken("--", isUnaryOperator() ? Token::Type::OperatorPrefixDec : Token::Type::OperatorSuffixDec);
|
|
else if(isUnaryOperator())
|
|
addOperatorToken(ch, Token::Type::OperatorUnarySub);
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorSub);
|
|
break;
|
|
case '=':
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken("==", Token::Type::OperatorEqual);
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorAssign);
|
|
break;
|
|
case '<':
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken("<=", Token::Type::OperatorSmallerEqual);
|
|
else if(tryEatNextCh(i, '<'))
|
|
{
|
|
if(tryEatNextCh(i, '<'))
|
|
{
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken("<<<=", Token::Type::OperatorAssignRol);
|
|
else
|
|
addOperatorToken("<<<", Token::Type::OperatorRol);
|
|
}
|
|
else if(tryEatNextCh(i, '='))
|
|
addOperatorToken("<<=", Token::Type::OperatorAssignShl);
|
|
else
|
|
addOperatorToken("<<", Token::Type::OperatorShl);
|
|
}
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorSmaller);
|
|
break;
|
|
case '>':
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken(">=", Token::Type::OperatorBiggerEqual);
|
|
else if(tryEatNextCh(i, '>'))
|
|
{
|
|
if(tryEatNextCh(i, '>'))
|
|
{
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken(">>>=", Token::Type::OperatorAssignRor);
|
|
else
|
|
addOperatorToken(">>>", Token::Type::OperatorRor);
|
|
}
|
|
else if(tryEatNextCh(i, '='))
|
|
addOperatorToken(">>=", Token::Type::OperatorAssignShr);
|
|
else
|
|
addOperatorToken(">>", Token::Type::OperatorShr);
|
|
}
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorBigger);
|
|
break;
|
|
case '&':
|
|
if(tryEatNextCh(i, '&'))
|
|
addOperatorToken("&&", Token::Type::OperatorLogicalAnd);
|
|
else if(tryEatNextCh(i, '='))
|
|
addOperatorToken("&=", Token::Type::OperatorAssignAnd);
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorAnd);
|
|
break;
|
|
case '^':
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken("^=", Token::Type::OperatorAssignXor);
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorXor);
|
|
break;
|
|
case '|':
|
|
if(tryEatNextCh(i, '|'))
|
|
addOperatorToken("||", Token::Type::OperatorLogicalOr);
|
|
else if(tryEatNextCh(i, '='))
|
|
addOperatorToken("|=", Token::Type::OperatorAssignOr);
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorOr);
|
|
break;
|
|
case '!':
|
|
if(tryEatNextCh(i, '='))
|
|
addOperatorToken("!=", Token::Type::OperatorNotEqual);
|
|
else
|
|
addOperatorToken(ch, Token::Type::OperatorLogicalNot);
|
|
break;
|
|
case ' ': //ignore spaces
|
|
case '\t': //ignore tabs
|
|
break;
|
|
default:
|
|
push_token(ch);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(mCurToken.length() != 0) //make sure the last token is added
|
|
{
|
|
mTokens.push_back(Token(mCurToken, resolveQuotedData()));
|
|
mCurToken.clear();
|
|
mCurTokenQuoted.clear();
|
|
}
|
|
}
|
|
|
|
void ExpressionParser::addOperatorToken(const String & data, Token::Type type)
|
|
{
|
|
if(mCurToken.length()) //add a new data token when there is data in the buffer
|
|
{
|
|
mTokens.push_back(Token(mCurToken, type == Token::Type::OpenBracket ? Token::Type::Function : resolveQuotedData()));
|
|
mCurToken.clear();
|
|
mCurTokenQuoted.clear();
|
|
}
|
|
mTokens.push_back(Token(data, type)); //add the operator token
|
|
}
|
|
|
|
ExpressionParser::Token::Type ExpressionParser::resolveQuotedData() const
|
|
{
|
|
auto allQuoted = std::find(mCurTokenQuoted.begin(), mCurTokenQuoted.end(), false) == mCurTokenQuoted.end();
|
|
return allQuoted ? Token::Type::QuotedData : Token::Type::Data;
|
|
}
|
|
|
|
bool ExpressionParser::isUnaryOperator() const
|
|
{
|
|
if(mCurToken.length()) //data before the operator means it is no unary operator
|
|
return false;
|
|
if(!mTokens.size()) //no tokens before the operator means it is an unary operator
|
|
return true;
|
|
auto lastType = mTokens.back().type();
|
|
//if the previous token is not data or a close bracket, this operator is a unary operator
|
|
return lastType != Token::Type::Data && lastType != Token::Type::QuotedData && lastType != Token::Type::CloseBracket;
|
|
}
|
|
|
|
void ExpressionParser::shuntingYard()
|
|
{
|
|
//Implementation of Dijkstra's Shunting-yard algorithm (https://en.wikipedia.org/wiki/Shunting-yard_algorithm)
|
|
std::vector<Token> queue;
|
|
std::vector<Token> stack;
|
|
auto len = mTokens.size();
|
|
queue.reserve(len);
|
|
stack.reserve(len);
|
|
//process the tokens
|
|
for(size_t i = 0; i < len; i++)
|
|
{
|
|
const auto & token = mTokens[i]; //Read a token
|
|
switch(token.type())
|
|
{
|
|
case Token::Type::Data: //If the token is a number, then push it to the output queue.
|
|
case Token::Type::QuotedData:
|
|
queue.push_back(token);
|
|
break;
|
|
case Token::Type::Function: //If the token is a function token, then push it onto the stack.
|
|
stack.push_back(token);
|
|
break;
|
|
case Token::Type::Comma: //If the token is a function argument separator (e.g., a comma):
|
|
while(true) //Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue.
|
|
{
|
|
if(stack.empty()) //If no left parentheses are encountered, either the separator was misplaced or parentheses were mismatched.
|
|
{
|
|
mIsValidExpression = false;
|
|
return;
|
|
}
|
|
const auto & curToken = stack.back();
|
|
if(curToken.type() == Token::Type::OpenBracket)
|
|
break;
|
|
queue.push_back(curToken);
|
|
stack.pop_back();
|
|
}
|
|
break;
|
|
case Token::Type::OpenBracket: //If the token is a left parenthesis (i.e. "("), then push it onto the stack.
|
|
stack.push_back(token);
|
|
break;
|
|
case Token::Type::CloseBracket: //If the token is a right parenthesis (i.e. ")"):
|
|
{
|
|
while(true) //Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue.
|
|
{
|
|
if(stack.empty()) //If the stack runs out without finding a left parenthesis, then there are mismatched parentheses.
|
|
{
|
|
mIsValidExpression = false;
|
|
return;
|
|
}
|
|
auto curToken = stack.back();
|
|
stack.pop_back(); //Pop the left parenthesis from the stack, but not onto the output queue.
|
|
if(curToken.type() == Token::Type::OpenBracket) //the bracket is already popped here
|
|
break;
|
|
queue.push_back(curToken);
|
|
}
|
|
auto size = stack.size();
|
|
if(size && stack[size - 1].type() == Token::Type::Function) //If the token at the top of the stack is a function token, pop it onto the output queue.
|
|
{
|
|
queue.push_back(stack[size - 1]);
|
|
stack.pop_back();
|
|
}
|
|
}
|
|
break;
|
|
default: //If the token is an operator, o1, then:
|
|
const auto & o1 = token;
|
|
while(!stack.empty()) //while there is an operator token o2, at the top of the operator stack and either
|
|
{
|
|
const auto & o2 = stack.back();
|
|
if(o2.isOperator() &&
|
|
(o1.associativity() == Token::Associativity::LeftToRight && o1.precedence() >= o2.precedence()) || //o1 is left-associative and its precedence is less than or equal to that of o2, or
|
|
(o1.associativity() == Token::Associativity::RightToLeft && o1.precedence() > o2.precedence())) //o1 is right associative, and has precedence less than that of o2,
|
|
{
|
|
queue.push_back(o2); //pop o2 off the operator stack, onto the output queue;
|
|
stack.pop_back();
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
stack.push_back(o1); //at the end of iteration push o1 onto the operator stack.
|
|
break;
|
|
}
|
|
}
|
|
//When there are no more tokens to read:
|
|
while(!stack.empty()) //While there are still operator tokens in the stack:
|
|
{
|
|
const auto & curToken = stack.back();
|
|
if(curToken.type() == Token::Type::OpenBracket || curToken.type() == Token::Type::CloseBracket) //If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses.
|
|
{
|
|
mIsValidExpression = false;
|
|
return;
|
|
}
|
|
queue.push_back(curToken); //Pop the operator onto the output queue.
|
|
stack.pop_back();
|
|
}
|
|
mPrefixTokens = std::move(queue);
|
|
}
|
|
|
|
#if defined(_WIN64) && (!defined(__clang__) || __clang_major__ > 3) // This produces an ICE under Clang <= 3.8; fixed in 5.0.
|
|
#include <intrin.h>
|
|
|
|
static unsigned long long umulhi(unsigned long long x, unsigned long long y)
|
|
{
|
|
unsigned __int64 res;
|
|
_umul128(x, y, &res);
|
|
return res;
|
|
}
|
|
|
|
static long long mulhi(long long x, long long y)
|
|
{
|
|
__int64 res;
|
|
_mul128(x, y, &res);
|
|
return res;
|
|
}
|
|
#else
|
|
static unsigned int umulhi(unsigned int x, unsigned int y)
|
|
{
|
|
return (unsigned int)(((unsigned long long)x * y) >> 32);
|
|
}
|
|
|
|
static int mulhi(int x, int y)
|
|
{
|
|
return (int)(((long long)x * y) >> 32);
|
|
}
|
|
#endif //_WIN64
|
|
|
|
template<typename T>
|
|
static bool operation(ExpressionParser::Token::Type type, T op1, T op2, T & result, bool signedcalc)
|
|
{
|
|
result = 0;
|
|
switch(type)
|
|
{
|
|
case ExpressionParser::Token::Type::OperatorUnarySub:
|
|
result = op1 * ~0;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorUnaryAdd:
|
|
result = +op1;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorNot:
|
|
result = ~op1;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorLogicalNot:
|
|
result = !op1 ? 1 : 0;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorMul:
|
|
result = op1 * op2;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorHiMul:
|
|
if(signedcalc)
|
|
result = mulhi(op1, op2);
|
|
else
|
|
result = umulhi(op1, op2);
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorDiv:
|
|
if(op2 == 0)
|
|
return false;
|
|
result = op1 / op2;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorMod:
|
|
if(op2 == 0)
|
|
return false;
|
|
result = op1 % op2;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAdd:
|
|
result = op1 + op2;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorSub:
|
|
result = op1 - op2;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorShl:
|
|
result = op1 << op2;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorShr:
|
|
result = op1 >> op2;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorRol:
|
|
#ifdef _WIN64
|
|
result = _rotl64(op1, int(op2) % 64);
|
|
#else
|
|
result = _rotl(op1, int(op2) % 32);
|
|
#endif
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorRor:
|
|
#ifdef _WIN64
|
|
result = _rotr64(op1, int(op2) % 64);
|
|
#else
|
|
result = _rotr(op1, int(op2) % 32);
|
|
#endif
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAnd:
|
|
result = op1 & op2;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorXor:
|
|
result = op1 ^ op2;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorOr:
|
|
result = op1 | op2;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorEqual:
|
|
result = op1 == op2 ? 1 : 0;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorNotEqual:
|
|
result = op1 != op2 ? 1 : 0;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorBigger:
|
|
result = op1 > op2 ? 1 : 0;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorSmaller:
|
|
result = op1 < op2 ? 1 : 0;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorBiggerEqual:
|
|
result = op1 >= op2 ? 1 : 0;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorSmallerEqual:
|
|
result = op1 <= op2 ? 1 : 0;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorLogicalAnd:
|
|
result = op1 && op2 ? 1 : 0;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorLogicalOr:
|
|
result = op1 || op2 ? 1 : 0;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorLogicalImpl:
|
|
result = !op1 || op2 ? 1 : 0;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool getAssignmentOperator(ExpressionParser::Token::Type type, ExpressionParser::Token::Type & result)
|
|
{
|
|
switch(type)
|
|
{
|
|
case ExpressionParser::Token::Type::OperatorAssign:
|
|
return false;
|
|
case ExpressionParser::Token::Type::OperatorAssignMul:
|
|
result = ExpressionParser::Token::Type::OperatorMul;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignHiMul:
|
|
result = ExpressionParser::Token::Type::OperatorHiMul;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignDiv:
|
|
result = ExpressionParser::Token::Type::OperatorDiv;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignMod:
|
|
result = ExpressionParser::Token::Type::OperatorMod;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignAdd:
|
|
result = ExpressionParser::Token::Type::OperatorAdd;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignSub:
|
|
result = ExpressionParser::Token::Type::OperatorSub;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignShl:
|
|
result = ExpressionParser::Token::Type::OperatorShl;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignShr:
|
|
result = ExpressionParser::Token::Type::OperatorShr;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignRol:
|
|
result = ExpressionParser::Token::Type::OperatorRol;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignRor:
|
|
result = ExpressionParser::Token::Type::OperatorRor;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignAnd:
|
|
result = ExpressionParser::Token::Type::OperatorAnd;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignXor:
|
|
result = ExpressionParser::Token::Type::OperatorXor;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorAssignOr:
|
|
result = ExpressionParser::Token::Type::OperatorOr;
|
|
break;
|
|
default:
|
|
__debugbreak();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool handleAssignment(const char* variable, duint resultv, bool silent, bool allowassign)
|
|
{
|
|
if(!allowassign)
|
|
return false;
|
|
bool destIsVar = false;
|
|
duint temp;
|
|
valfromstring_noexpr(variable, &temp, true, true, nullptr, &destIsVar, nullptr); //there is no return check on this because the destination might not exist yet
|
|
if(!destIsVar)
|
|
destIsVar = vargettype(variable, nullptr);
|
|
if(!destIsVar || !valtostring(variable, resultv, true))
|
|
{
|
|
duint value;
|
|
if(valfromstring(variable, &value)) //if the var is a value already it's an invalid destination
|
|
{
|
|
if(!silent)
|
|
dprintf(QT_TRANSLATE_NOOP("DBG", "invalid dest \"%s\"\n"), variable);
|
|
return false;
|
|
}
|
|
varnew(variable, resultv, VAR_USER);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<typename T>
|
|
static bool evalOperation(ExpressionParser::Token::Type type, const ExpressionParser::EvalValue & op1, const ExpressionParser::EvalValue & op2, ExpressionParser::EvalValue & result, bool signedcalc, bool silent, bool baseonly, bool allowassign)
|
|
{
|
|
switch(type)
|
|
{
|
|
case ExpressionParser::Token::Type::OperatorAssign:
|
|
case ExpressionParser::Token::Type::OperatorAssignMul:
|
|
case ExpressionParser::Token::Type::OperatorAssignHiMul:
|
|
case ExpressionParser::Token::Type::OperatorAssignDiv:
|
|
case ExpressionParser::Token::Type::OperatorAssignMod:
|
|
case ExpressionParser::Token::Type::OperatorAssignAdd:
|
|
case ExpressionParser::Token::Type::OperatorAssignSub:
|
|
case ExpressionParser::Token::Type::OperatorAssignShl:
|
|
case ExpressionParser::Token::Type::OperatorAssignShr:
|
|
case ExpressionParser::Token::Type::OperatorAssignRol:
|
|
case ExpressionParser::Token::Type::OperatorAssignRor:
|
|
case ExpressionParser::Token::Type::OperatorAssignAnd:
|
|
case ExpressionParser::Token::Type::OperatorAssignXor:
|
|
case ExpressionParser::Token::Type::OperatorAssignOr:
|
|
{
|
|
if(op1.evaluated)
|
|
return false;
|
|
ExpressionParser::EvalValue newvalue(0);
|
|
ExpressionParser::Token::Type assop;
|
|
if(getAssignmentOperator(type, assop))
|
|
{
|
|
if(!evalOperation<T>(assop, op1, op2, newvalue, signedcalc, silent, baseonly, allowassign))
|
|
return false;
|
|
}
|
|
else
|
|
newvalue = op2;
|
|
duint resultv;
|
|
if(!newvalue.DoEvaluate(resultv, silent, baseonly))
|
|
return false;
|
|
if(!handleAssignment(op1.data.c_str(), resultv, silent, allowassign))
|
|
return false;
|
|
result = ExpressionParser::EvalValue(resultv);
|
|
}
|
|
break;
|
|
|
|
case ExpressionParser::Token::Type::OperatorPrefixInc:
|
|
case ExpressionParser::Token::Type::OperatorPrefixDec:
|
|
case ExpressionParser::Token::Type::OperatorSuffixInc:
|
|
case ExpressionParser::Token::Type::OperatorSuffixDec:
|
|
{
|
|
if(op1.evaluated)
|
|
return false;
|
|
duint op1v;
|
|
if(!op1.DoEvaluate(op1v, silent, baseonly))
|
|
return false;
|
|
duint resultv;
|
|
switch(type)
|
|
{
|
|
case ExpressionParser::Token::Type::OperatorPrefixInc:
|
|
resultv = ++op1v;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorPrefixDec:
|
|
resultv = --op1v;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorSuffixInc:
|
|
resultv = op1v++;
|
|
break;
|
|
case ExpressionParser::Token::Type::OperatorSuffixDec:
|
|
resultv = op1v--;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
if(!handleAssignment(op1.data.c_str(), op1v, silent, allowassign))
|
|
return false;
|
|
result = ExpressionParser::EvalValue(resultv);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
duint op1v, op2v;
|
|
if(op1.isString || op2.isString)
|
|
return false;
|
|
if(!op1.DoEvaluate(op1v, silent, baseonly) || !op2.DoEvaluate(op2v, silent, baseonly))
|
|
return false;
|
|
T resultv;
|
|
if(!operation<T>(type, T(op1v), T(op2v), resultv, signedcalc))
|
|
return false;
|
|
result = ExpressionParser::EvalValue(duint(resultv));
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ExpressionParser::unsignedOperation(Token::Type type, const EvalValue & op1, const EvalValue & op2, EvalValue & result, bool silent, bool baseonly, bool allowassign) const
|
|
{
|
|
return evalOperation<duint>(type, op1, op2, result, false, silent, baseonly, allowassign);
|
|
}
|
|
|
|
bool ExpressionParser::signedOperation(Token::Type type, const EvalValue & op1, const EvalValue & op2, EvalValue & result, bool silent, bool baseonly, bool allowassign) const
|
|
{
|
|
return evalOperation<dsint>(type, op1, op2, result, true, silent, baseonly, allowassign);
|
|
}
|
|
|
|
bool ExpressionParser::Calculate(duint & value, bool signedcalc, bool allowassign, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly) const
|
|
{
|
|
EvalValue evalue(0);
|
|
if(!Calculate(evalue, signedcalc, allowassign, silent, baseonly, value_size, isvar, hexonly))
|
|
return false;
|
|
|
|
if(evalue.isString)
|
|
{
|
|
if(!silent)
|
|
dprintf(QT_TRANSLATE_NOOP("DBG", "Expression evaluated to a string: \"%s\"\n"), StringUtils::Escape(evalue.data).c_str());
|
|
return false;
|
|
}
|
|
return evalue.DoEvaluate(value, silent, baseonly, value_size, isvar, hexonly);
|
|
}
|
|
|
|
bool ExpressionParser::Calculate(EvalValue & value, bool signedcalc, bool allowassign, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly) const
|
|
{
|
|
if(!mPrefixTokens.size() || !mIsValidExpression)
|
|
return false;
|
|
std::vector<EvalValue> stack;
|
|
stack.reserve(mPrefixTokens.size());
|
|
//calculate the result from the RPN queue
|
|
for(const auto & token : mPrefixTokens)
|
|
{
|
|
if(token.isOperator())
|
|
{
|
|
EvalValue op1(0);
|
|
EvalValue op2(0);
|
|
EvalValue result(0);
|
|
bool operationSuccess;
|
|
auto type = token.type();
|
|
switch(type)
|
|
{
|
|
case Token::Type::OperatorUnarySub:
|
|
case Token::Type::OperatorUnaryAdd:
|
|
case Token::Type::OperatorNot:
|
|
case Token::Type::OperatorLogicalNot:
|
|
case Token::Type::OperatorPrefixInc:
|
|
case Token::Type::OperatorPrefixDec:
|
|
case Token::Type::OperatorSuffixInc:
|
|
case Token::Type::OperatorSuffixDec:
|
|
if(stack.empty())
|
|
return false;
|
|
op1 = stack.back();
|
|
stack.pop_back();
|
|
if(signedcalc)
|
|
operationSuccess = signedOperation(type, op1, op2, result, silent, baseonly, allowassign);
|
|
else
|
|
operationSuccess = unsignedOperation(type, op1, op2, result, silent, baseonly, allowassign);
|
|
if(!operationSuccess)
|
|
return false;
|
|
stack.push_back(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::OperatorRol:
|
|
case Token::Type::OperatorRor:
|
|
case Token::Type::OperatorAnd:
|
|
case Token::Type::OperatorXor:
|
|
case Token::Type::OperatorOr:
|
|
case Token::Type::OperatorEqual:
|
|
case Token::Type::OperatorNotEqual:
|
|
case Token::Type::OperatorBigger:
|
|
case Token::Type::OperatorSmaller:
|
|
case Token::Type::OperatorBiggerEqual:
|
|
case Token::Type::OperatorSmallerEqual:
|
|
case Token::Type::OperatorLogicalAnd:
|
|
case Token::Type::OperatorLogicalOr:
|
|
case Token::Type::OperatorLogicalImpl:
|
|
case Token::Type::OperatorAssign:
|
|
case Token::Type::OperatorAssignMul:
|
|
case Token::Type::OperatorAssignHiMul:
|
|
case Token::Type::OperatorAssignDiv:
|
|
case Token::Type::OperatorAssignMod:
|
|
case Token::Type::OperatorAssignAdd:
|
|
case Token::Type::OperatorAssignSub:
|
|
case Token::Type::OperatorAssignShl:
|
|
case Token::Type::OperatorAssignShr:
|
|
case Token::Type::OperatorAssignRol:
|
|
case Token::Type::OperatorAssignRor:
|
|
case Token::Type::OperatorAssignAnd:
|
|
case Token::Type::OperatorAssignXor:
|
|
case Token::Type::OperatorAssignOr:
|
|
if(stack.size() < 2)
|
|
return false;
|
|
op2 = stack.back();
|
|
stack.pop_back();
|
|
op1 = stack.back();
|
|
stack.pop_back();
|
|
if(signedcalc)
|
|
operationSuccess = signedOperation(type, op1, op2, result, silent, baseonly, allowassign);
|
|
else
|
|
operationSuccess = unsignedOperation(type, op1, op2, result, silent, baseonly, allowassign);
|
|
if(!operationSuccess)
|
|
return false;
|
|
stack.push_back(result);
|
|
break;
|
|
case Token::Type::Error:
|
|
return false;
|
|
default: //do nothing
|
|
break;
|
|
}
|
|
}
|
|
else if(token.type() == Token::Type::Function)
|
|
{
|
|
const auto & name = token.data();
|
|
ValueType returnType;
|
|
std::vector<ValueType> argTypes;
|
|
if(!ExpressionFunctions::GetType(name, returnType, argTypes))
|
|
return false;
|
|
if(int(stack.size()) < argTypes.size())
|
|
return false;
|
|
std::vector<ExpressionValue> argv;
|
|
argv.resize(argTypes.size());
|
|
for(size_t i = 0; i < argTypes.size(); i++)
|
|
{
|
|
const auto & argType = argTypes[argTypes.size() - i - 1];
|
|
auto & top = stack[stack.size() - i - 1];
|
|
ExpressionValue arg;
|
|
if(top.isString)
|
|
{
|
|
arg = { ValueTypeString, 0, StringValue{ top.data.c_str(), false } };
|
|
}
|
|
else if(top.evaluated)
|
|
{
|
|
arg = { ValueTypeNumber, top.value };
|
|
}
|
|
else
|
|
{
|
|
duint result;
|
|
if(!top.DoEvaluate(result, silent, baseonly, value_size, isvar, hexonly))
|
|
return false;
|
|
arg = { ValueTypeNumber, result };
|
|
}
|
|
|
|
if(arg.type != argType && argType != ValueTypeAny)
|
|
{
|
|
if(!silent)
|
|
{
|
|
auto typeName = [](ValueType t) -> String
|
|
{
|
|
switch(t)
|
|
{
|
|
case ValueTypeNumber:
|
|
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "number"));
|
|
case ValueTypeString:
|
|
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "string"));
|
|
}
|
|
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "invalid"));
|
|
};
|
|
String argValueStr;
|
|
if(arg.type == ValueTypeNumber)
|
|
{
|
|
argValueStr = StringUtils::sprintf("0x%p", arg.number);
|
|
}
|
|
else if(arg.type == ValueTypeString)
|
|
{
|
|
argValueStr = "\"" + StringUtils::Escape(arg.string.ptr) + "\"";
|
|
}
|
|
else
|
|
{
|
|
argValueStr = "???";
|
|
}
|
|
String signature = name;
|
|
signature += "(";
|
|
for(size_t j = 0; j < argTypes.size(); j++)
|
|
{
|
|
if(j > 0)
|
|
{
|
|
signature += ", ";
|
|
}
|
|
signature += typeName(argTypes[j]);
|
|
}
|
|
signature += ")";
|
|
dprintf(QT_TRANSLATE_NOOP("DBG", "Expression function %s argument %d/%d (%s) type mismatch (expected %s, got %s)!\n"),
|
|
signature.c_str(),
|
|
argTypes.size() - i,
|
|
argTypes.size(),
|
|
argValueStr.c_str(),
|
|
typeName(argType).c_str(),
|
|
typeName(arg.type).c_str()
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
argv[argTypes.size() - i - 1] = arg;
|
|
}
|
|
|
|
ExpressionValue result = { ValueTypeNumber, 0 };
|
|
if(!ExpressionFunctions::Call(name, result, argv))
|
|
return false;
|
|
|
|
if(result.type == ValueTypeAny)
|
|
return false;
|
|
|
|
for(size_t i = 0; i < argv.size(); i++)
|
|
{
|
|
stack.pop_back();
|
|
}
|
|
if(result.type == ValueTypeString)
|
|
{
|
|
stack.push_back(EvalValue(result.string.ptr, true));
|
|
if(result.string.isOwner)
|
|
BridgeFree((void*)result.string.ptr);
|
|
}
|
|
else
|
|
stack.push_back(EvalValue(result.number));
|
|
}
|
|
else
|
|
stack.push_back(EvalValue(token.data(), token.type() == Token::Type::QuotedData));
|
|
}
|
|
if(stack.size() != 1) //there should only be one value left on the stack
|
|
return false;
|
|
value = stack.back();
|
|
return true;
|
|
}
|