summaryrefslogtreecommitdiff
path: root/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2019-03-02 14:47:26 -0600
committerJordan Woyak <jordan.woyak@gmail.com>2019-10-11 18:12:18 -0500
commitca7ce674500b7664ed049ac1019a2e40636187dc (patch)
tree6cd8f5563ce9ee43a645ebbf43b13ee9eee6989f /Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
parentc8b2188e1972f14aec24ac66908a641dbdbd106f (diff)
ExpressionParser/DolphinQt: Added parse results to UI.
Diffstat (limited to 'Source/Core/InputCommon/ControlReference/ExpressionParser.cpp')
-rw-r--r--Source/Core/InputCommon/ControlReference/ExpressionParser.cpp145
1 files changed, 94 insertions, 51 deletions
diff --git a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
index 08278154f4..a686e8fcc4 100644
--- a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
+++ b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
@@ -11,6 +11,7 @@
#include <utility>
#include <vector>
+#include "Common/Common.h"
#include "Common/StringUtil.h"
#include "InputCommon/ControlReference/ExpressionParser.h"
@@ -138,7 +139,7 @@ std::string Lexer::FetchWordChars()
return "";
// Valid word characters:
- std::regex rx("[a-z0-9_]", std::regex_constants::icase);
+ std::regex rx(R"([a-z\d_])", std::regex_constants::icase);
return FetchCharsWhile([&rx](char c) { return std::regex_match(std::string(1, c), rx); });
}
@@ -180,7 +181,10 @@ Token Lexer::GetRealLiteral(char c)
value += c;
value += FetchCharsWhile([](char c) { return isdigit(c, std::locale::classic()) || ('.' == c); });
- return Token(TOK_LITERAL, value);
+ if (std::regex_match(value, std::regex(R"(\d+(\.\d+)?)")))
+ return Token(TOK_LITERAL, value);
+
+ return Token(TOK_INVALID);
}
Token Lexer::NextToken()
@@ -267,8 +271,7 @@ ParseStatus Lexer::Tokenize(std::vector<Token>& tokens)
class ControlExpression : public Expression
{
public:
- // Keep a shared_ptr to the device so the control pointer doesn't become invalid
- // TODO: This is causing devices to be destructed after backends are shutdown:
+ // Keep a shared_ptr to the device so the control pointer doesn't become invalid.
std::shared_ptr<Device> m_device;
explicit ControlExpression(ControlQualifier qualifier_) : qualifier(qualifier_) {}
@@ -384,7 +387,7 @@ public:
operator std::string() const override
{
- return OpName(op) + "(" + (std::string)(*lhs) + ", " + (std::string)(*rhs) + ")";
+ return OpName(op) + "(" + std::string(*lhs) + ", " + std::string(*rhs) + ")";
}
};
@@ -422,12 +425,13 @@ private:
const ControlState m_value{};
};
-std::unique_ptr<LiteralExpression> MakeLiteralExpression(std::string name)
+ParseResult MakeLiteralExpression(Token token)
{
- // If TryParse fails we'll just get a Zero.
ControlState val{};
- TryParse(name, &val);
- return std::make_unique<LiteralReal>(val);
+ if (TryParse(token.data, &val))
+ return ParseResult::MakeSuccessfulResult(std::make_unique<LiteralReal>(val));
+ else
+ return ParseResult::MakeErrorResult(token, _trans("Invalid literal."));
}
class VariableExpression : public Expression
@@ -520,45 +524,63 @@ ControlState* ControlEnvironment::GetVariablePtr(const std::string& name)
return &m_variables[name];
}
-struct ParseResult
+ParseResult ParseResult::MakeEmptyResult()
{
- ParseResult(ParseStatus status_, std::unique_ptr<Expression>&& expr_ = {})
- : status(status_), expr(std::move(expr_))
- {
- }
+ ParseResult result;
+ result.status = ParseStatus::EmptyExpression;
+ return result;
+}
- ParseStatus status;
- std::unique_ptr<Expression> expr;
-};
+ParseResult ParseResult::MakeSuccessfulResult(std::unique_ptr<Expression>&& expr)
+{
+ ParseResult result;
+ result.status = ParseStatus::Successful;
+ result.expr = std::move(expr);
+ return result;
+}
+
+ParseResult ParseResult::MakeErrorResult(Token token, std::string description)
+{
+ ParseResult result;
+ result.status = ParseStatus::SyntaxError;
+ result.token = std::move(token);
+ result.description = std::move(description);
+ return result;
+}
class Parser
{
public:
- explicit Parser(std::vector<Token> tokens_) : tokens(tokens_) { m_it = tokens.begin(); }
+ explicit Parser(const std::vector<Token>& tokens_) : tokens(tokens_) { m_it = tokens.begin(); }
ParseResult Parse()
{
ParseResult result = ParseToplevel();
+ if (ParseStatus::Successful != result.status)
+ return result;
+
if (Peek().type == TOK_EOF)
return result;
- return {ParseStatus::SyntaxError};
+ return ParseResult::MakeErrorResult(Peek(), _trans("Expected EOF."));
}
private:
struct FunctionArguments
{
- FunctionArguments(ParseStatus status_, std::vector<std::unique_ptr<Expression>>&& args_ = {})
- : status(status_), args(std::move(args_))
+ FunctionArguments(ParseResult&& result_, std::vector<std::unique_ptr<Expression>>&& args_ = {})
+ : result(std::move(result_)), args(std::move(args_))
{
}
- ParseStatus status;
+ // Note: expression member isn't being used.
+ ParseResult result;
+
std::vector<std::unique_ptr<Expression>> args;
};
- std::vector<Token> tokens;
- std::vector<Token>::iterator m_it;
+ const std::vector<Token>& tokens;
+ std::vector<Token>::const_iterator m_it;
Token Chew()
{
@@ -585,10 +607,10 @@ private:
// Single argument with no parens (useful for unary ! function)
auto arg = ParseAtom(Chew());
if (ParseStatus::Successful != arg.status)
- return {ParseStatus::SyntaxError};
+ return {std::move(arg)};
args.emplace_back(std::move(arg.expr));
- return {ParseStatus::Successful, std::move(args)};
+ return {ParseResult::MakeSuccessfulResult({}), std::move(args)};
}
// Chew the L-Paren
@@ -598,7 +620,7 @@ private:
if (TOK_RPAREN == Peek().type)
{
Chew();
- return {ParseStatus::Successful};
+ return {ParseResult::MakeSuccessfulResult({})};
}
while (true)
@@ -607,18 +629,18 @@ private:
// Grab an expression, but stop at comma.
auto arg = ParseBinary(BinaryOperatorPrecedence(TOK_COMMA));
if (ParseStatus::Successful != arg.status)
- return {ParseStatus::SyntaxError};
+ return {std::move(arg)};
args.emplace_back(std::move(arg.expr));
// Right paren is the end of our arguments.
const Token tok = Chew();
if (TOK_RPAREN == tok.type)
- return {ParseStatus::Successful, std::move(args)};
+ return {ParseResult::MakeSuccessfulResult({}), std::move(args)};
// Comma before the next argument.
if (TOK_COMMA != tok.type)
- return {ParseStatus::SyntaxError};
+ return {ParseResult::MakeErrorResult(tok, _trans("Expected comma."))};
}
}
@@ -629,29 +651,36 @@ private:
case TOK_FUNCTION:
{
auto func = MakeFunctionExpression(tok.data);
+
+ if (!func)
+ return ParseResult::MakeErrorResult(tok, _trans("Unknown function."));
+
auto args = ParseFunctionArguments();
- if (ParseStatus::Successful != args.status)
- return {ParseStatus::SyntaxError};
+ if (ParseStatus::Successful != args.result.status)
+ return std::move(args.result);
if (!func->SetArguments(std::move(args.args)))
- return {ParseStatus::SyntaxError};
+ {
+ // TODO: It would be nice to output how many arguments are expected.
+ return ParseResult::MakeErrorResult(tok, _trans("Wrong number of arguments."));
+ }
- return {ParseStatus::Successful, std::move(func)};
+ return ParseResult::MakeSuccessfulResult(std::move(func));
}
case TOK_CONTROL:
{
ControlQualifier cq;
cq.FromString(tok.data);
- return {ParseStatus::Successful, std::make_unique<ControlExpression>(cq)};
+ return ParseResult::MakeSuccessfulResult(std::make_unique<ControlExpression>(cq));
}
case TOK_LITERAL:
{
- return {ParseStatus::Successful, MakeLiteralExpression(tok.data)};
+ return MakeLiteralExpression(tok);
}
case TOK_VARIABLE:
{
- return {ParseStatus::Successful, std::make_unique<VariableExpression>(tok.data)};
+ return ParseResult::MakeSuccessfulResult(std::make_unique<VariableExpression>(tok.data));
}
case TOK_LPAREN:
{
@@ -661,10 +690,15 @@ private:
{
// An atom was expected but we got a subtraction symbol.
// Interpret it as a unary minus function.
- return ParseAtom(Token(TOK_FUNCTION, "minus"));
+
+ // Make sure to copy the existing string position values for proper error results.
+ Token func = tok;
+ func.type = TOK_FUNCTION;
+ func.data = "minus";
+ return ParseAtom(std::move(func));
}
default:
- return {ParseStatus::SyntaxError};
+ return ParseResult::MakeErrorResult(tok, _trans("Expected start of expression."));
}
}
@@ -718,7 +752,7 @@ private:
expr = std::make_unique<BinaryExpression>(tok.type, std::move(expr), std::move(rhs.expr));
}
- return {ParseStatus::Successful, std::move(expr)};
+ return ParseResult::MakeSuccessfulResult(std::move(expr));
}
ParseResult ParseParens()
@@ -728,9 +762,10 @@ private:
if (result.status != ParseStatus::Successful)
return result;
- if (!Expects(TOK_RPAREN))
+ const auto rparen = Chew();
+ if (rparen.type != TOK_RPAREN)
{
- return {ParseStatus::SyntaxError};
+ return ParseResult::MakeErrorResult(rparen, _trans("Expected closing paren."));
}
return result;
@@ -739,15 +774,20 @@ private:
ParseResult ParseToplevel() { return ParseBinary(); }
}; // namespace ExpressionParser
+ParseResult ParseTokens(const std::vector<Token>& tokens)
+{
+ return Parser(tokens).Parse();
+}
+
static ParseResult ParseComplexExpression(const std::string& str)
{
Lexer l(str);
std::vector<Token> tokens;
- ParseStatus tokenize_status = l.Tokenize(tokens);
+ const ParseStatus tokenize_status = l.Tokenize(tokens);
if (tokenize_status != ParseStatus::Successful)
- return {tokenize_status};
+ return ParseResult::MakeErrorResult(Token(TOK_INVALID), _trans("Tokenizing failed."));
- return Parser(std::move(tokens)).Parse();
+ return ParseTokens(tokens);
}
static std::unique_ptr<Expression> ParseBarewordExpression(const std::string& str)
@@ -759,21 +799,24 @@ static std::unique_ptr<Expression> ParseBarewordExpression(const std::string& st
return std::make_unique<ControlExpression>(qualifier);
}
-std::pair<ParseStatus, std::unique_ptr<Expression>> ParseExpression(const std::string& str)
+ParseResult ParseExpression(const std::string& str)
{
if (StripSpaces(str).empty())
- return std::make_pair(ParseStatus::EmptyExpression, nullptr);
+ return ParseResult::MakeEmptyResult();
auto bareword_expr = ParseBarewordExpression(str);
ParseResult complex_result = ParseComplexExpression(str);
if (complex_result.status != ParseStatus::Successful)
{
- return std::make_pair(complex_result.status, std::move(bareword_expr));
+ // This is a bit odd.
+ // Return the error status of the complex expression with the fallback barewords expression.
+ complex_result.expr = std::move(bareword_expr);
+ return complex_result;
}
- auto combined_expr = std::make_unique<CoalesceExpression>(std::move(bareword_expr),
- std::move(complex_result.expr));
- return std::make_pair(complex_result.status, std::move(combined_expr));
+ complex_result.expr = std::make_unique<CoalesceExpression>(std::move(bareword_expr),
+ std::move(complex_result.expr));
+ return complex_result;
}
} // namespace ciface::ExpressionParser