Store the number of expression function arguments in the token

This commit is contained in:
Duncan Ogilvie 2023-11-07 16:49:30 +01:00
parent 735d3ca5f9
commit 50d7d988f6
2 changed files with 51 additions and 17 deletions

View File

@ -215,10 +215,10 @@ void ExpressionParser::tokenize()
addOperatorToken(ch, Token::Type::Comma);
break;
case '(':
addOperatorToken(ch, Token::Type::OpenBracket);
addOperatorToken(ch, Token::Type::OpenParen);
break;
case ')':
addOperatorToken(ch, Token::Type::CloseBracket);
addOperatorToken(ch, Token::Type::CloseParen);
break;
case '~':
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
{
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();
mCurTokenQuoted.clear();
}
@ -388,7 +395,7 @@ bool ExpressionParser::isUnaryOperator() const
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;
return lastType != Token::Type::Data && lastType != Token::Type::QuotedData && lastType != Token::Type::CloseParen;
}
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)
std::vector<Token> queue;
std::vector<Token> stack;
std::vector<duint> argCount;
auto len = mTokens.size();
queue.reserve(len);
stack.reserve(len);
@ -410,9 +418,18 @@ void ExpressionParser::shuntingYard()
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;
// 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;
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.
@ -421,16 +438,20 @@ void ExpressionParser::shuntingYard()
return;
}
const auto & curToken = stack.back();
if(curToken.type() == Token::Type::OpenBracket)
if(curToken.type() == Token::Type::OpenParen)
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.
if(!argCount.empty()) // A comma increases the argument count
argCount.back()++;
}
break;
case Token::Type::OpenParen: //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. ")"):
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.
{
@ -441,14 +462,16 @@ void ExpressionParser::shuntingYard()
}
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
if(curToken.type() == Token::Type::OpenParen) //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.
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.
{
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();
}
}
@ -476,7 +499,7 @@ void ExpressionParser::shuntingYard()
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.
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;
return;

View File

@ -17,7 +17,7 @@ public:
explicit EvalValue(duint value)
: evaluated(true), value(value) {}
explicit EvalValue(const String & data, bool isString)
EvalValue(const String & data, bool 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
@ -61,8 +61,8 @@ public:
QuotedData,
Function,
Comma,
OpenBracket,
CloseBracket,
OpenParen,
CloseParen,
OperatorUnarySub,
OperatorUnaryAdd,
@ -133,6 +133,16 @@ public:
return mType;
}
duint info() const
{
return mInfo;
}
void setInfo(duint info)
{
mInfo = info;
}
Associativity associativity() const;
int precedence() const;
bool isOperator() const;
@ -140,6 +150,7 @@ public:
private:
String mData;
Type mType;
duint mInfo = 0;
};
private: