Store the number of expression function arguments in the token
This commit is contained in:
parent
735d3ca5f9
commit
50d7d988f6
|
@ -215,10 +215,10 @@ void ExpressionParser::tokenize()
|
||||||
addOperatorToken(ch, Token::Type::Comma);
|
addOperatorToken(ch, Token::Type::Comma);
|
||||||
break;
|
break;
|
||||||
case '(':
|
case '(':
|
||||||
addOperatorToken(ch, Token::Type::OpenBracket);
|
addOperatorToken(ch, Token::Type::OpenParen);
|
||||||
break;
|
break;
|
||||||
case ')':
|
case ')':
|
||||||
addOperatorToken(ch, Token::Type::CloseBracket);
|
addOperatorToken(ch, Token::Type::CloseParen);
|
||||||
break;
|
break;
|
||||||
case '~':
|
case '~':
|
||||||
addOperatorToken(ch, Token::Type::OperatorNot);
|
addOperatorToken(ch, Token::Type::OperatorNot);
|
||||||
|
@ -367,7 +367,14 @@ void ExpressionParser::addOperatorToken(const String & data, Token::Type type)
|
||||||
{
|
{
|
||||||
if(mCurToken.length()) //add a new data token when there is data in the buffer
|
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()));
|
if(type == Token::Type::OpenParen)
|
||||||
|
{
|
||||||
|
mTokens.push_back(Token(mCurToken, Token::Type::Function));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mTokens.push_back(Token(mCurToken, resolveQuotedData()));
|
||||||
|
}
|
||||||
mCurToken.clear();
|
mCurToken.clear();
|
||||||
mCurTokenQuoted.clear();
|
mCurTokenQuoted.clear();
|
||||||
}
|
}
|
||||||
|
@ -388,7 +395,7 @@ bool ExpressionParser::isUnaryOperator() const
|
||||||
return true;
|
return true;
|
||||||
auto lastType = mTokens.back().type();
|
auto lastType = mTokens.back().type();
|
||||||
//if the previous token is not data or a close bracket, this operator is a unary operator
|
//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;
|
return lastType != Token::Type::Data && lastType != Token::Type::QuotedData && lastType != Token::Type::CloseParen;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpressionParser::shuntingYard()
|
void ExpressionParser::shuntingYard()
|
||||||
|
@ -396,6 +403,7 @@ void ExpressionParser::shuntingYard()
|
||||||
//Implementation of Dijkstra's Shunting-yard algorithm (https://en.wikipedia.org/wiki/Shunting-yard_algorithm)
|
//Implementation of Dijkstra's Shunting-yard algorithm (https://en.wikipedia.org/wiki/Shunting-yard_algorithm)
|
||||||
std::vector<Token> queue;
|
std::vector<Token> queue;
|
||||||
std::vector<Token> stack;
|
std::vector<Token> stack;
|
||||||
|
std::vector<duint> argCount;
|
||||||
auto len = mTokens.size();
|
auto len = mTokens.size();
|
||||||
queue.reserve(len);
|
queue.reserve(len);
|
||||||
stack.reserve(len);
|
stack.reserve(len);
|
||||||
|
@ -410,9 +418,18 @@ void ExpressionParser::shuntingYard()
|
||||||
queue.push_back(token);
|
queue.push_back(token);
|
||||||
break;
|
break;
|
||||||
case Token::Type::Function: //If the token is a function token, then push it onto the stack.
|
case Token::Type::Function: //If the token is a function token, then push it onto the stack.
|
||||||
|
{
|
||||||
stack.push_back(token);
|
stack.push_back(token);
|
||||||
|
|
||||||
|
// Unless the syntax is 'fn()' there is always at least one argument
|
||||||
|
if(i + 2 < mTokens.size() && mTokens[i + 1].type() == Token::Type::OpenParen && mTokens[i + 2].type() == Token::Type::CloseParen)
|
||||||
|
argCount.push_back(0);
|
||||||
|
else
|
||||||
|
argCount.push_back(1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Token::Type::Comma: //If the token is a function argument separator (e.g., a comma):
|
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.
|
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.
|
if(stack.empty()) //If no left parentheses are encountered, either the separator was misplaced or parentheses were mismatched.
|
||||||
|
@ -421,16 +438,20 @@ void ExpressionParser::shuntingYard()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto & curToken = stack.back();
|
const auto & curToken = stack.back();
|
||||||
if(curToken.type() == Token::Type::OpenBracket)
|
if(curToken.type() == Token::Type::OpenParen)
|
||||||
break;
|
break;
|
||||||
queue.push_back(curToken);
|
queue.push_back(curToken);
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!argCount.empty()) // A comma increases the argument count
|
||||||
|
argCount.back()++;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Token::Type::OpenBracket: //If the token is a left parenthesis (i.e. "("), then push it onto the stack.
|
case Token::Type::OpenParen: //If the token is a left parenthesis (i.e. "("), then push it onto the stack.
|
||||||
stack.push_back(token);
|
stack.push_back(token);
|
||||||
break;
|
break;
|
||||||
case Token::Type::CloseBracket: //If the token is a right parenthesis (i.e. ")"):
|
case Token::Type::CloseParen: //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.
|
while(true) //Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue.
|
||||||
{
|
{
|
||||||
|
@ -441,14 +462,16 @@ void ExpressionParser::shuntingYard()
|
||||||
}
|
}
|
||||||
auto curToken = stack.back();
|
auto curToken = stack.back();
|
||||||
stack.pop_back(); //Pop the left parenthesis from the stack, but not onto the output queue.
|
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
|
if(curToken.type() == Token::Type::OpenParen) //the bracket is already popped here
|
||||||
break;
|
break;
|
||||||
queue.push_back(curToken);
|
queue.push_back(curToken);
|
||||||
}
|
}
|
||||||
auto size = stack.size();
|
if(!stack.empty() && stack.back().type() == Token::Type::Function) //If the token at the top of the stack is a function token, pop it onto the output queue.
|
||||||
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]);
|
// Propagate the argument count as extra information
|
||||||
|
stack.back().setInfo(argCount.back());
|
||||||
|
argCount.pop_back();
|
||||||
|
queue.push_back(stack.back());
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -476,7 +499,7 @@ void ExpressionParser::shuntingYard()
|
||||||
while(!stack.empty()) //While there are still operator tokens in the stack:
|
while(!stack.empty()) //While there are still operator tokens in the stack:
|
||||||
{
|
{
|
||||||
const auto & curToken = stack.back();
|
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.
|
if(curToken.type() == Token::Type::OpenParen || curToken.type() == Token::Type::CloseParen) //If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses.
|
||||||
{
|
{
|
||||||
mIsValidExpression = false;
|
mIsValidExpression = false;
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -17,7 +17,7 @@ public:
|
||||||
explicit EvalValue(duint value)
|
explicit EvalValue(duint value)
|
||||||
: evaluated(true), value(value) {}
|
: evaluated(true), value(value) {}
|
||||||
|
|
||||||
explicit EvalValue(const String & data, bool isString)
|
EvalValue(const String & data, bool isString)
|
||||||
: evaluated(false), data(data), isString(isString) {}
|
: evaluated(false), data(data), isString(isString) {}
|
||||||
|
|
||||||
bool DoEvaluate(duint & result, bool silent = true, bool baseonly = false, int* value_size = nullptr, bool* isvar = nullptr, bool* hexonly = nullptr) const
|
bool DoEvaluate(duint & result, bool silent = true, bool baseonly = false, int* value_size = nullptr, bool* isvar = nullptr, bool* hexonly = nullptr) const
|
||||||
|
@ -61,8 +61,8 @@ public:
|
||||||
QuotedData,
|
QuotedData,
|
||||||
Function,
|
Function,
|
||||||
Comma,
|
Comma,
|
||||||
OpenBracket,
|
OpenParen,
|
||||||
CloseBracket,
|
CloseParen,
|
||||||
|
|
||||||
OperatorUnarySub,
|
OperatorUnarySub,
|
||||||
OperatorUnaryAdd,
|
OperatorUnaryAdd,
|
||||||
|
@ -133,6 +133,16 @@ public:
|
||||||
return mType;
|
return mType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
duint info() const
|
||||||
|
{
|
||||||
|
return mInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInfo(duint info)
|
||||||
|
{
|
||||||
|
mInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
Associativity associativity() const;
|
Associativity associativity() const;
|
||||||
int precedence() const;
|
int precedence() const;
|
||||||
bool isOperator() const;
|
bool isOperator() const;
|
||||||
|
@ -140,6 +150,7 @@ public:
|
||||||
private:
|
private:
|
||||||
String mData;
|
String mData;
|
||||||
Type mType;
|
Type mType;
|
||||||
|
duint mInfo = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
Loading…
Reference in New Issue