mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-09 23:59:27 +01:00
ExpressionParser/DolphinQt: Added parse results to UI.
This commit is contained in:
parent
c8b2188e19
commit
ca7ce67450
@ -4,6 +4,7 @@
|
||||
|
||||
#include "DolphinQt/Config/Mapping/IOWindow.h"
|
||||
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#include <QComboBox>
|
||||
@ -11,13 +12,13 @@
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QMessageBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPushButton>
|
||||
#include <QSlider>
|
||||
#include <QSpinBox>
|
||||
#include <QSyntaxHighlighter>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Core/Core.h"
|
||||
@ -35,6 +36,7 @@ constexpr int SLIDER_TICK_COUNT = 100;
|
||||
|
||||
namespace
|
||||
{
|
||||
// TODO: Make sure these functions return colors that will be visible in the current theme.
|
||||
QTextCharFormat GetSpecialCharFormat()
|
||||
{
|
||||
QTextCharFormat format;
|
||||
@ -85,56 +87,78 @@ QTextCharFormat GetFunctionCharFormat()
|
||||
format.setForeground(QBrush{Qt::darkCyan});
|
||||
return format;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class SyntaxHighlighter : public QSyntaxHighlighter
|
||||
ControlExpressionSyntaxHighlighter::ControlExpressionSyntaxHighlighter(QTextDocument* parent,
|
||||
QLineEdit* result)
|
||||
: QSyntaxHighlighter(parent), m_result_text(result)
|
||||
{
|
||||
public:
|
||||
SyntaxHighlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) {}
|
||||
}
|
||||
|
||||
void highlightBlock(const QString& text) final override
|
||||
void ControlExpressionSyntaxHighlighter::highlightBlock(const QString& text)
|
||||
{
|
||||
// TODO: This is going to result in improper highlighting with non-ascii characters:
|
||||
ciface::ExpressionParser::Lexer lexer(text.toStdString());
|
||||
|
||||
std::vector<ciface::ExpressionParser::Token> tokens;
|
||||
const auto tokenize_status = lexer.Tokenize(tokens);
|
||||
|
||||
using ciface::ExpressionParser::TokenType;
|
||||
|
||||
for (auto& token : tokens)
|
||||
{
|
||||
// TODO: This is going to result in improper highlighting with non-ascii characters:
|
||||
ciface::ExpressionParser::Lexer lexer(text.toStdString());
|
||||
std::optional<QTextCharFormat> char_format;
|
||||
|
||||
std::vector<ciface::ExpressionParser::Token> tokens;
|
||||
lexer.Tokenize(tokens);
|
||||
|
||||
using ciface::ExpressionParser::TokenType;
|
||||
|
||||
for (auto& token : tokens)
|
||||
switch (token.type)
|
||||
{
|
||||
switch (token.type)
|
||||
{
|
||||
case TokenType::TOK_INVALID:
|
||||
setFormat(token.string_position, token.string_length, GetInvalidCharFormat());
|
||||
break;
|
||||
case TokenType::TOK_LPAREN:
|
||||
case TokenType::TOK_RPAREN:
|
||||
case TokenType::TOK_COMMA:
|
||||
setFormat(token.string_position, token.string_length, GetSpecialCharFormat());
|
||||
break;
|
||||
case TokenType::TOK_LITERAL:
|
||||
setFormat(token.string_position, token.string_length, GetLiteralCharFormat());
|
||||
break;
|
||||
case TokenType::TOK_CONTROL:
|
||||
setFormat(token.string_position, token.string_length, GetControlCharFormat());
|
||||
break;
|
||||
case TokenType::TOK_FUNCTION:
|
||||
setFormat(token.string_position, token.string_length, GetFunctionCharFormat());
|
||||
break;
|
||||
case TokenType::TOK_VARIABLE:
|
||||
setFormat(token.string_position, token.string_length, GetVariableCharFormat());
|
||||
break;
|
||||
default:
|
||||
if (token.IsBinaryOperator())
|
||||
setFormat(token.string_position, token.string_length, GetOperatorCharFormat());
|
||||
break;
|
||||
}
|
||||
case TokenType::TOK_INVALID:
|
||||
char_format = GetInvalidCharFormat();
|
||||
break;
|
||||
case TokenType::TOK_LPAREN:
|
||||
case TokenType::TOK_RPAREN:
|
||||
case TokenType::TOK_COMMA:
|
||||
char_format = GetSpecialCharFormat();
|
||||
break;
|
||||
case TokenType::TOK_LITERAL:
|
||||
char_format = GetLiteralCharFormat();
|
||||
break;
|
||||
case TokenType::TOK_CONTROL:
|
||||
char_format = GetControlCharFormat();
|
||||
break;
|
||||
case TokenType::TOK_FUNCTION:
|
||||
char_format = GetFunctionCharFormat();
|
||||
break;
|
||||
case TokenType::TOK_VARIABLE:
|
||||
char_format = GetVariableCharFormat();
|
||||
break;
|
||||
default:
|
||||
if (token.IsBinaryOperator())
|
||||
char_format = GetOperatorCharFormat();
|
||||
break;
|
||||
}
|
||||
|
||||
if (char_format.has_value())
|
||||
setFormat(int(token.string_position), int(token.string_length), *char_format);
|
||||
}
|
||||
|
||||
if (ciface::ExpressionParser::ParseStatus::Successful != tokenize_status)
|
||||
{
|
||||
m_result_text->setText(tr("Invalid Token."));
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto parse_status = ciface::ExpressionParser::ParseTokens(tokens);
|
||||
|
||||
m_result_text->setText(
|
||||
QString::fromStdString(parse_status.description.value_or(_trans("Success."))));
|
||||
|
||||
if (ciface::ExpressionParser::ParseStatus::Successful != parse_status.status)
|
||||
{
|
||||
const auto token = *parse_status.token;
|
||||
setFormat(int(token.string_position), int(token.string_length), GetInvalidCharFormat());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
}
|
||||
|
||||
IOWindow::IOWindow(QWidget* parent, ControllerEmu::EmulatedController* controller,
|
||||
ControlReference* ref, IOWindow::Type type)
|
||||
@ -165,9 +189,12 @@ void IOWindow::CreateMainLayout()
|
||||
m_range_slider = new QSlider(Qt::Horizontal);
|
||||
m_range_spinbox = new QSpinBox();
|
||||
|
||||
m_parse_text = new QLineEdit();
|
||||
m_parse_text->setReadOnly(true);
|
||||
|
||||
m_expression_text = new QPlainTextEdit();
|
||||
m_expression_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
new SyntaxHighlighter(m_expression_text->document());
|
||||
new ControlExpressionSyntaxHighlighter(m_expression_text->document(), m_parse_text);
|
||||
|
||||
m_operators_combo = new QComboBox();
|
||||
m_operators_combo->addItem(tr("Operators"));
|
||||
@ -234,6 +261,7 @@ void IOWindow::CreateMainLayout()
|
||||
|
||||
m_main_layout->addLayout(hbox, 2);
|
||||
m_main_layout->addWidget(m_expression_text, 1);
|
||||
m_main_layout->addWidget(m_parse_text);
|
||||
|
||||
// Button Box
|
||||
m_main_layout->addWidget(m_button_box);
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
#include <QSyntaxHighlighter>
|
||||
|
||||
#include "Common/Flag.h"
|
||||
#include "InputCommon/ControllerInterface/Device.h"
|
||||
@ -14,6 +15,7 @@ class ControlReference;
|
||||
class QAbstractButton;
|
||||
class QComboBox;
|
||||
class QDialogButtonBox;
|
||||
class QLineEdit;
|
||||
class QListWidget;
|
||||
class QVBoxLayout;
|
||||
class QWidget;
|
||||
@ -27,6 +29,19 @@ namespace ControllerEmu
|
||||
class EmulatedController;
|
||||
}
|
||||
|
||||
class ControlExpressionSyntaxHighlighter final : public QSyntaxHighlighter
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ControlExpressionSyntaxHighlighter(QTextDocument* parent, QLineEdit* result);
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString& text) final override;
|
||||
|
||||
private:
|
||||
QLineEdit* const m_result_text;
|
||||
};
|
||||
|
||||
class IOWindow final : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -81,6 +96,7 @@ private:
|
||||
|
||||
// Textarea
|
||||
QPlainTextEdit* m_expression_text;
|
||||
QLineEdit* m_parse_text;
|
||||
|
||||
// Buttonbox
|
||||
QDialogButtonBox* m_button_box;
|
||||
|
@ -54,7 +54,9 @@ std::string ControlReference::GetExpression() const
|
||||
void ControlReference::SetExpression(std::string expr)
|
||||
{
|
||||
m_expression = std::move(expr);
|
||||
std::tie(m_parse_status, m_parsed_expression) = ParseExpression(m_expression);
|
||||
auto parse_result = ParseExpression(m_expression);
|
||||
m_parse_status = parse_result.status;
|
||||
m_parsed_expression = std::move(parse_result.expr);
|
||||
}
|
||||
|
||||
ControlReference::ControlReference() : range(1), m_parsed_expression(nullptr)
|
||||
|
@ -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
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "InputCommon/ControllerInterface/Device.h"
|
||||
@ -49,7 +50,7 @@ public:
|
||||
std::size_t string_position = 0;
|
||||
std::size_t string_length = 0;
|
||||
|
||||
Token(TokenType type_);
|
||||
explicit Token(TokenType type_);
|
||||
Token(TokenType type_, std::string data_);
|
||||
|
||||
bool IsBinaryOperator() const;
|
||||
@ -166,5 +167,26 @@ public:
|
||||
virtual operator std::string() const = 0;
|
||||
};
|
||||
|
||||
std::pair<ParseStatus, std::unique_ptr<Expression>> ParseExpression(const std::string& expr);
|
||||
class ParseResult
|
||||
{
|
||||
public:
|
||||
static ParseResult MakeEmptyResult();
|
||||
static ParseResult MakeSuccessfulResult(std::unique_ptr<Expression>&& expr);
|
||||
static ParseResult MakeErrorResult(Token token, std::string description);
|
||||
|
||||
ParseStatus status;
|
||||
std::unique_ptr<Expression> expr;
|
||||
|
||||
// Used for parse errors:
|
||||
// TODO: This should probably be moved elsewhere:
|
||||
std::optional<Token> token;
|
||||
std::optional<std::string> description;
|
||||
|
||||
private:
|
||||
ParseResult() = default;
|
||||
};
|
||||
|
||||
ParseResult ParseExpression(const std::string& expr);
|
||||
ParseResult ParseTokens(const std::vector<Token>& tokens);
|
||||
|
||||
} // namespace ciface::ExpressionParser
|
||||
|
@ -17,19 +17,6 @@ constexpr ControlState CONDITION_THRESHOLD = 0.5;
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using FSec = std::chrono::duration<ControlState>;
|
||||
|
||||
// TODO: Return an oscillating value to make it apparent something was spelled wrong?
|
||||
class UnknownFunctionExpression : public FunctionExpression
|
||||
{
|
||||
private:
|
||||
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ControlState GetValue() const override { return 0.0; }
|
||||
void SetValue(ControlState value) override {}
|
||||
std::string GetFuncName() const override { return "unknown"; }
|
||||
};
|
||||
|
||||
// usage: !toggle(toggle_state_input, [clear_state_input])
|
||||
class ToggleExpression : public FunctionExpression
|
||||
{
|
||||
@ -503,7 +490,7 @@ std::unique_ptr<FunctionExpression> MakeFunctionExpression(std::string name)
|
||||
else if ("pulse" == name)
|
||||
return std::make_unique<PulseExpression>();
|
||||
else
|
||||
return std::make_unique<UnknownFunctionExpression>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int FunctionExpression::CountNumControls() const
|
||||
|
Loading…
x
Reference in New Issue
Block a user