1
0
Fork 0

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); 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;

View File

@ -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: