Merge pull request #5577 from ligfx/separateexpressionparsingandbinding

ControlReference/ExpressionParser: separate parsing from binding
This commit is contained in:
Leo Lam 2017-09-15 19:11:57 +02:00 committed by GitHub
commit 2b4bf8662a
9 changed files with 200 additions and 202 deletions

View File

@ -344,7 +344,7 @@ void HotkeyManager::LoadDefaults(const ControllerInterface& ciface)
auto set_key_expression = [this](int index, const std::string& expression) { auto set_key_expression = [this](int index, const std::string& expression) {
m_keys[FindGroupByID(index)] m_keys[FindGroupByID(index)]
->controls[GetIndexForGroup(FindGroupByID(index), index)] ->controls[GetIndexForGroup(FindGroupByID(index), index)]
->control_ref->expression = expression; ->control_ref->SetExpression(expression);
}; };
// General hotkeys // General hotkeys

View File

@ -110,7 +110,7 @@ void IOWindow::CreateMainLayout()
void IOWindow::Update() void IOWindow::Update()
{ {
m_expression_text->setPlainText(QString::fromStdString(m_reference->expression)); m_expression_text->setPlainText(QString::fromStdString(m_reference->GetExpression()));
m_range_spinbox->setValue(m_reference->range * SLIDER_TICK_COUNT); m_range_spinbox->setValue(m_reference->range * SLIDER_TICK_COUNT);
m_range_slider->setValue(m_reference->range * SLIDER_TICK_COUNT); m_range_slider->setValue(m_reference->range * SLIDER_TICK_COUNT);
@ -164,7 +164,7 @@ void IOWindow::OnDialogButtonPressed(QAbstractButton* button)
return; return;
} }
m_reference->expression = m_expression_text->toPlainText().toStdString(); m_reference->SetExpression(m_expression_text->toPlainText().toStdString());
if (button != m_apply_button) if (button != m_apply_button)
accept(); accept();

View File

@ -27,7 +27,7 @@ static QString EscapeAmpersand(QString&& string)
} }
MappingButton::MappingButton(MappingWidget* widget, ControlReference* ref) MappingButton::MappingButton(MappingWidget* widget, ControlReference* ref)
: ElidedButton(EscapeAmpersand(QString::fromStdString(ref->expression))), m_parent(widget), : ElidedButton(EscapeAmpersand(QString::fromStdString(ref->GetExpression()))), m_parent(widget),
m_reference(ref) m_reference(ref)
{ {
Connect(); Connect();
@ -66,7 +66,7 @@ void MappingButton::OnButtonPressed()
if (!expr.isEmpty()) if (!expr.isEmpty())
{ {
m_reference->expression = expr.toStdString(); m_reference->SetExpression(expr.toStdString());
Update(); Update();
} }
else else
@ -78,13 +78,13 @@ void MappingButton::OnButtonPressed()
void MappingButton::OnButtonTimeout() void MappingButton::OnButtonTimeout()
{ {
setText(EscapeAmpersand(QString::fromStdString(m_reference->expression))); setText(EscapeAmpersand(QString::fromStdString(m_reference->GetExpression())));
} }
void MappingButton::Clear() void MappingButton::Clear()
{ {
m_parent->Update(); m_parent->Update();
m_reference->expression.clear(); m_reference->SetExpression("");
Update(); Update();
} }
@ -92,7 +92,7 @@ void MappingButton::Update()
{ {
const auto lock = ControllerEmu::EmulatedController::GetStateLock(); const auto lock = ControllerEmu::EmulatedController::GetStateLock();
m_reference->UpdateReference(g_controller_interface, m_parent->GetParent()->GetDeviceQualifier()); m_reference->UpdateReference(g_controller_interface, m_parent->GetParent()->GetDeviceQualifier());
setText(EscapeAmpersand(QString::fromStdString(m_reference->expression))); setText(EscapeAmpersand(QString::fromStdString(m_reference->GetExpression())));
m_parent->SaveSettings(); m_parent->SaveSettings();
} }

View File

@ -70,7 +70,7 @@
#include "InputCommon/ControllerInterface/Device.h" #include "InputCommon/ControllerInterface/Device.h"
#include "InputCommon/InputConfig.h" #include "InputCommon/InputConfig.h"
using namespace ciface::ExpressionParser; using ciface::ExpressionParser::ParseStatus;
void InputConfigDialog::ConfigExtension(wxCommandEvent& event) void InputConfigDialog::ConfigExtension(wxCommandEvent& event)
{ {
@ -243,7 +243,7 @@ ControlButton::ControlButton(wxWindow* const parent, ControlReference* const _re
m_configured_width(FromDIP(width)) m_configured_width(FromDIP(width))
{ {
if (label.empty()) if (label.empty())
SetLabelText(StrToWxStr(_ref->expression)); SetLabelText(StrToWxStr(_ref->GetExpression()));
else else
SetLabel(StrToWxStr(label)); SetLabel(StrToWxStr(label));
} }
@ -336,7 +336,7 @@ void ControlDialog::SelectControl(const std::string& name)
void ControlDialog::UpdateGUI() void ControlDialog::UpdateGUI()
{ {
// update textbox // update textbox
textctrl->SetValue(StrToWxStr(control_reference->expression)); textctrl->SetValue(StrToWxStr(control_reference->GetExpression()));
// updates the "bound controls:" label // updates the "bound controls:" label
m_bound_label->SetLabel( m_bound_label->SetLabel(
@ -347,11 +347,12 @@ void ControlDialog::UpdateGUI()
case ParseStatus::SyntaxError: case ParseStatus::SyntaxError:
m_error_label->SetLabel(_("Syntax error")); m_error_label->SetLabel(_("Syntax error"));
break; break;
case ParseStatus::NoDevice: case ParseStatus::Successful:
m_error_label->SetLabel(_("Device not found")); m_error_label->SetLabel(control_reference->BoundCount() > 0 ? "" : _("Device not found"));
break; break;
default: case ParseStatus::EmptyExpression:
m_error_label->SetLabel(""); m_error_label->SetLabel("");
break;
} }
}; };
@ -364,7 +365,7 @@ void InputConfigDialog::UpdateGUI()
{ {
for (ControlButton* button : cgBox->control_buttons) for (ControlButton* button : cgBox->control_buttons)
{ {
button->SetLabelText(StrToWxStr(button->control_reference->expression)); button->SetLabelText(StrToWxStr(button->control_reference->GetExpression()));
} }
for (PadSetting* padSetting : cgBox->options) for (PadSetting* padSetting : cgBox->options)
@ -399,7 +400,7 @@ void InputConfigDialog::LoadDefaults(wxCommandEvent&)
bool ControlDialog::Validate() bool ControlDialog::Validate()
{ {
control_reference->expression = WxStrToStr(textctrl->GetValue()); control_reference->SetExpression(WxStrToStr(textctrl->GetValue()));
const auto lock = ControllerEmu::EmulatedController::GetStateLock(); const auto lock = ControllerEmu::EmulatedController::GetStateLock();
control_reference->UpdateReference(g_controller_interface, control_reference->UpdateReference(g_controller_interface,
@ -408,7 +409,7 @@ bool ControlDialog::Validate()
UpdateGUI(); UpdateGUI();
const auto parse_status = control_reference->GetParseStatus(); const auto parse_status = control_reference->GetParseStatus();
return parse_status == ParseStatus::Successful || parse_status == ParseStatus::NoDevice; return parse_status == ParseStatus::Successful || parse_status == ParseStatus::EmptyExpression;
} }
void InputConfigDialog::SetDevice(wxCommandEvent&) void InputConfigDialog::SetDevice(wxCommandEvent&)
@ -438,7 +439,7 @@ void ControlDialog::SetDevice(wxCommandEvent&)
void ControlDialog::ClearControl(wxCommandEvent&) void ControlDialog::ClearControl(wxCommandEvent&)
{ {
control_reference->expression.clear(); control_reference->SetExpression("");
const auto lock = ControllerEmu::EmulatedController::GetStateLock(); const auto lock = ControllerEmu::EmulatedController::GetStateLock();
control_reference->UpdateReference(g_controller_interface, control_reference->UpdateReference(g_controller_interface,
@ -497,7 +498,7 @@ void ControlDialog::SetSelectedControl(wxCommandEvent&)
return; return;
textctrl->WriteText(expr); textctrl->WriteText(expr);
control_reference->expression = textctrl->GetValue(); control_reference->SetExpression(WxStrToStr(textctrl->GetValue()));
const auto lock = ControllerEmu::EmulatedController::GetStateLock(); const auto lock = ControllerEmu::EmulatedController::GetStateLock();
control_reference->UpdateReference(g_controller_interface, control_reference->UpdateReference(g_controller_interface,
@ -533,7 +534,7 @@ void ControlDialog::AppendControl(wxCommandEvent& event)
} }
textctrl->WriteText(expr); textctrl->WriteText(expr);
control_reference->expression = textctrl->GetValue(); control_reference->SetExpression(WxStrToStr(textctrl->GetValue()));
const auto lock = ControllerEmu::EmulatedController::GetStateLock(); const auto lock = ControllerEmu::EmulatedController::GetStateLock();
control_reference->UpdateReference(g_controller_interface, control_reference->UpdateReference(g_controller_interface,
@ -637,7 +638,7 @@ void InputConfigDialog::ConfigControl(wxEvent& event)
void InputConfigDialog::ClearControl(wxEvent& event) void InputConfigDialog::ClearControl(wxEvent& event)
{ {
ControlButton* const btn = (ControlButton*)event.GetEventObject(); ControlButton* const btn = (ControlButton*)event.GetEventObject();
btn->control_reference->expression.clear(); btn->control_reference->SetExpression("");
btn->control_reference->range = 1.0; btn->control_reference->range = 1.0;
controller->UpdateReferences(g_controller_interface); controller->UpdateReferences(g_controller_interface);
@ -716,7 +717,7 @@ bool InputConfigDialog::DetectButton(ControlButton* button)
wxString control_name = ctrl->GetName(); wxString control_name = ctrl->GetName();
wxString expr; wxString expr;
GetExpressionForControl(expr, control_name); GetExpressionForControl(expr, control_name);
button->control_reference->expression = expr; button->control_reference->SetExpression(WxStrToStr(expr));
const auto lock = ControllerEmu::EmulatedController::GetStateLock(); const auto lock = ControllerEmu::EmulatedController::GetStateLock();
button->control_reference->UpdateReference(g_controller_interface, button->control_reference->UpdateReference(g_controller_interface,
controller->default_device); controller->default_device);

View File

@ -25,21 +25,20 @@ bool ControlReference::InputGateOn()
// UpdateReference // UpdateReference
// //
// Updates a controlreference's binded devices/controls // Updates a controlreference's binded devices/controls
// need to call this to re-parse a control reference's expression after changing it // need to call this to re-bind a control reference after changing its expression
// //
void ControlReference::UpdateReference(const ciface::Core::DeviceContainer& devices, void ControlReference::UpdateReference(const ciface::Core::DeviceContainer& devices,
const ciface::Core::DeviceQualifier& default_device) const ciface::Core::DeviceQualifier& default_device)
{ {
Expression* expr;
ControlFinder finder(devices, default_device, IsInput()); ControlFinder finder(devices, default_device, IsInput());
m_parse_status = ParseExpression(expression, finder, &expr); if (m_parsed_expression)
m_parsed_expression.reset(expr); m_parsed_expression->UpdateReferences(finder);
} }
int ControlReference::BoundCount() const int ControlReference::BoundCount() const
{ {
if (m_parsed_expression) if (m_parsed_expression)
return m_parsed_expression->num_controls; return m_parsed_expression->CountNumControls();
else else
return 0; return 0;
} }
@ -49,6 +48,17 @@ ParseStatus ControlReference::GetParseStatus() const
return m_parse_status; return m_parse_status;
} }
std::string ControlReference::GetExpression() const
{
return m_expression;
}
void ControlReference::SetExpression(std::string expr)
{
m_expression = std::move(expr);
std::tie(m_parse_status, m_parsed_expression) = ParseExpression(m_expression);
}
ControlReference::ControlReference() : range(1), m_parsed_expression(nullptr) ControlReference::ControlReference() : range(1), m_parsed_expression(nullptr)
{ {
} }

View File

@ -34,12 +34,14 @@ public:
ciface::ExpressionParser::ParseStatus GetParseStatus() const; ciface::ExpressionParser::ParseStatus GetParseStatus() const;
void UpdateReference(const ciface::Core::DeviceContainer& devices, void UpdateReference(const ciface::Core::DeviceContainer& devices,
const ciface::Core::DeviceQualifier& default_device); const ciface::Core::DeviceQualifier& default_device);
std::string GetExpression() const;
void SetExpression(std::string expr);
ControlState range; ControlState range;
std::string expression;
protected: protected:
ControlReference(); ControlReference();
std::string m_expression;
std::unique_ptr<ciface::ExpressionParser::Expression> m_parsed_expression; std::unique_ptr<ciface::ExpressionParser::Expression> m_parsed_expression;
ciface::ExpressionParser::ParseStatus m_parse_status; ciface::ExpressionParser::ParseStatus m_parse_status;
}; };

View File

@ -10,6 +10,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "Common/StringUtil.h"
#include "InputCommon/ControlReference/ExpressionParser.h" #include "InputCommon/ControlReference/ExpressionParser.h"
using namespace ciface::Core; using namespace ciface::Core;
@ -207,64 +208,42 @@ public:
} }
}; };
class ExpressionNode class ControlExpression : public Expression
{
public:
virtual ~ExpressionNode() {}
virtual ControlState GetValue() const { return 0; }
virtual void SetValue(ControlState state) {}
virtual int CountNumControls() const { return 0; }
virtual operator std::string() const { return ""; }
};
class DummyExpression : public ExpressionNode
{
public:
std::string name;
DummyExpression(const std::string& name_) : name(name_) {}
ControlState GetValue() const override { return 0.0; }
void SetValue(ControlState value) override {}
int CountNumControls() const override { return 0; }
operator std::string() const override { return "`" + name + "`"; }
};
class ControlExpression : public ExpressionNode
{ {
public: public:
ControlQualifier qualifier; ControlQualifier qualifier;
Device::Control* control; Device::Control* control = nullptr;
// Keep a shared_ptr to the device so the control pointer doesn't become invalid
ControlExpression(ControlQualifier qualifier_, std::shared_ptr<Device> device,
Device::Control* control_)
: qualifier(qualifier_), control(control_), m_device(device)
{
}
ControlState GetValue() const override { return control->ToInput()->GetState(); }
void SetValue(ControlState value) override { control->ToOutput()->SetState(value); }
int CountNumControls() const override { return 1; }
operator std::string() const override { return "`" + (std::string)qualifier + "`"; }
private:
std::shared_ptr<Device> m_device; std::shared_ptr<Device> m_device;
explicit ControlExpression(ControlQualifier qualifier_) : qualifier(qualifier_) {}
ControlState GetValue() const override { return control ? control->ToInput()->GetState() : 0.0; }
void SetValue(ControlState value) override
{
if (control)
control->ToOutput()->SetState(value);
}
int CountNumControls() const override { return control ? 1 : 0; }
void UpdateReferences(ControlFinder& finder) override
{
m_device = finder.FindDevice(qualifier);
control = finder.FindControl(qualifier);
}
operator std::string() const override { return "`" + static_cast<std::string>(qualifier) + "`"; }
}; };
class BinaryExpression : public ExpressionNode class BinaryExpression : public Expression
{ {
public: public:
TokenType op; TokenType op;
ExpressionNode* lhs; std::unique_ptr<Expression> lhs;
ExpressionNode* rhs; std::unique_ptr<Expression> rhs;
BinaryExpression(TokenType op_, ExpressionNode* lhs_, ExpressionNode* rhs_) BinaryExpression(TokenType op_, std::unique_ptr<Expression>&& lhs_,
: op(op_), lhs(lhs_), rhs(rhs_) std::unique_ptr<Expression>&& rhs_)
: op(op_), lhs(std::move(lhs_)), rhs(std::move(rhs_))
{ {
} }
virtual ~BinaryExpression()
{
delete lhs;
delete rhs;
}
ControlState GetValue() const override ControlState GetValue() const override
{ {
@ -297,20 +276,28 @@ public:
return lhs->CountNumControls() + rhs->CountNumControls(); return lhs->CountNumControls() + rhs->CountNumControls();
} }
void UpdateReferences(ControlFinder& finder) override
{
lhs->UpdateReferences(finder);
rhs->UpdateReferences(finder);
}
operator std::string() const override operator std::string() const override
{ {
return OpName(op) + "(" + (std::string)(*lhs) + ", " + (std::string)(*rhs) + ")"; return OpName(op) + "(" + (std::string)(*lhs) + ", " + (std::string)(*rhs) + ")";
} }
}; };
class UnaryExpression : public ExpressionNode class UnaryExpression : public Expression
{ {
public: public:
TokenType op; TokenType op;
ExpressionNode* inner; std::unique_ptr<Expression> inner;
UnaryExpression(TokenType op_, ExpressionNode* inner_) : op(op_), inner(inner_) {} UnaryExpression(TokenType op_, std::unique_ptr<Expression>&& inner_)
virtual ~UnaryExpression() { delete inner; } : op(op_), inner(std::move(inner_))
{
}
ControlState GetValue() const override ControlState GetValue() const override
{ {
ControlState value = inner->GetValue(); ControlState value = inner->GetValue();
@ -338,9 +325,50 @@ public:
} }
int CountNumControls() const override { return inner->CountNumControls(); } int CountNumControls() const override { return inner->CountNumControls(); }
void UpdateReferences(ControlFinder& finder) override { inner->UpdateReferences(finder); }
operator std::string() const override { return OpName(op) + "(" + (std::string)(*inner) + ")"; } operator std::string() const override { return OpName(op) + "(" + (std::string)(*inner) + ")"; }
}; };
// This class proxies all methods to its either left-hand child if it has bound controls, or its
// right-hand child. Its intended use is for supporting old-style barewords expressions.
class CoalesceExpression : public Expression
{
public:
CoalesceExpression(std::unique_ptr<Expression>&& lhs, std::unique_ptr<Expression>&& rhs)
: m_lhs(std::move(lhs)), m_rhs(std::move(rhs))
{
}
ControlState GetValue() const override { return GetActiveChild()->GetValue(); }
void SetValue(ControlState value) override
{
m_lhs->SetValue(GetActiveChild() == m_lhs ? value : 0.0);
m_rhs->SetValue(GetActiveChild() == m_rhs ? value : 0.0);
}
int CountNumControls() const override { return GetActiveChild()->CountNumControls(); }
operator std::string() const override
{
return "Coalesce(" + static_cast<std::string>(*m_lhs) + ", " +
static_cast<std::string>(*m_rhs) + ')';
}
void UpdateReferences(ControlFinder& finder) override
{
m_lhs->UpdateReferences(finder);
m_rhs->UpdateReferences(finder);
}
private:
const std::unique_ptr<Expression>& GetActiveChild() const
{
return m_lhs->CountNumControls() > 0 ? m_lhs : m_rhs;
}
std::unique_ptr<Expression> m_lhs;
std::unique_ptr<Expression> m_rhs;
};
std::shared_ptr<Device> ControlFinder::FindDevice(ControlQualifier qualifier) const std::shared_ptr<Device> ControlFinder::FindDevice(ControlQualifier qualifier) const
{ {
if (qualifier.has_device) if (qualifier.has_device)
@ -361,29 +389,25 @@ Device::Control* ControlFinder::FindControl(ControlQualifier qualifier) const
return device->FindOutput(qualifier.control_name); return device->FindOutput(qualifier.control_name);
} }
struct ParseResult
{
ParseResult(ParseStatus status_, std::unique_ptr<Expression>&& expr_ = {})
: status(status_), expr(std::move(expr_))
{
}
ParseStatus status;
std::unique_ptr<Expression> expr;
};
class Parser class Parser
{ {
public: public:
Parser(std::vector<Token> tokens_, ControlFinder& finder_) : tokens(tokens_), finder(finder_) explicit Parser(std::vector<Token> tokens_) : tokens(tokens_) { m_it = tokens.begin(); }
{ ParseResult Parse() { return Toplevel(); }
m_it = tokens.begin();
}
ParseStatus Parse(Expression** expr_out)
{
ExpressionNode* node;
ParseStatus status = Toplevel(&node);
if (status != ParseStatus::Successful)
return status;
*expr_out = new Expression(node);
return ParseStatus::Successful;
}
private: private:
std::vector<Token> tokens; std::vector<Token> tokens;
std::vector<Token>::iterator m_it; std::vector<Token>::iterator m_it;
ControlFinder& finder;
Token Chew() { return *m_it++; } Token Chew() { return *m_it++; }
Token Peek() { return *m_it; } Token Peek() { return *m_it; }
@ -393,28 +417,17 @@ private:
return tok.type == type; return tok.type == type;
} }
ParseStatus Atom(ExpressionNode** expr_out) ParseResult Atom()
{ {
Token tok = Chew(); Token tok = Chew();
switch (tok.type) switch (tok.type)
{ {
case TOK_CONTROL: case TOK_CONTROL:
{ return {ParseStatus::Successful, std::make_unique<ControlExpression>(tok.qualifier)};
std::shared_ptr<Device> device = finder.FindDevice(tok.qualifier);
Device::Control* control = finder.FindControl(tok.qualifier);
if (control == nullptr)
{
*expr_out = new DummyExpression(tok.qualifier);
return ParseStatus::NoDevice;
}
*expr_out = new ControlExpression(tok.qualifier, device, control);
return ParseStatus::Successful;
}
case TOK_LPAREN: case TOK_LPAREN:
return Paren(expr_out); return Paren();
default: default:
return ParseStatus::SyntaxError; return {ParseStatus::SyntaxError};
} }
} }
@ -429,20 +442,19 @@ private:
} }
} }
ParseStatus Unary(ExpressionNode** expr_out) ParseResult Unary()
{ {
if (IsUnaryExpression(Peek().type)) if (IsUnaryExpression(Peek().type))
{ {
Token tok = Chew(); Token tok = Chew();
ExpressionNode* atom_expr; ParseResult result = Atom();
ParseStatus status = Atom(&atom_expr); if (result.status == ParseStatus::SyntaxError)
if (status == ParseStatus::SyntaxError) return result;
return status; return {ParseStatus::Successful,
*expr_out = new UnaryExpression(tok.type, atom_expr); std::make_unique<UnaryExpression>(tok.type, std::move(result.expr))};
return ParseStatus::Successful;
} }
return Atom(expr_out); return Atom();
} }
bool IsBinaryToken(TokenType type) bool IsBinaryToken(TokenType type)
@ -458,113 +470,83 @@ private:
} }
} }
ParseStatus Binary(ExpressionNode** expr_out) ParseResult Binary()
{ {
ParseStatus status = Unary(expr_out); ParseResult result = Unary();
if (status == ParseStatus::SyntaxError) if (result.status == ParseStatus::SyntaxError)
return status; return result;
std::unique_ptr<Expression> expr = std::move(result.expr);
while (IsBinaryToken(Peek().type)) while (IsBinaryToken(Peek().type))
{ {
Token tok = Chew(); Token tok = Chew();
ExpressionNode* unary_expr; ParseResult unary_result = Unary();
status = Unary(&unary_expr); if (unary_result.status == ParseStatus::SyntaxError)
if (status == ParseStatus::SyntaxError)
{ {
delete *expr_out; return unary_result;
return status;
} }
*expr_out = new BinaryExpression(tok.type, *expr_out, unary_expr); expr = std::make_unique<BinaryExpression>(tok.type, std::move(expr),
std::move(unary_result.expr));
} }
return ParseStatus::Successful; return {ParseStatus::Successful, std::move(expr)};
} }
ParseStatus Paren(ExpressionNode** expr_out) ParseResult Paren()
{ {
ParseStatus status;
// lparen already chewed // lparen already chewed
if ((status = Toplevel(expr_out)) != ParseStatus::Successful) ParseResult result = Toplevel();
return status; if (result.status != ParseStatus::Successful)
return result;
if (!Expects(TOK_RPAREN)) if (!Expects(TOK_RPAREN))
{ {
delete *expr_out; return {ParseStatus::SyntaxError};
return ParseStatus::SyntaxError;
} }
return ParseStatus::Successful; return result;
} }
ParseStatus Toplevel(ExpressionNode** expr_out) { return Binary(expr_out); } ParseResult Toplevel() { return Binary(); }
}; };
ControlState Expression::GetValue() const static ParseResult ParseComplexExpression(const std::string& str)
{ {
return node->GetValue();
}
void Expression::SetValue(ControlState value)
{
node->SetValue(value);
}
Expression::Expression(ExpressionNode* node_)
{
node = node_;
num_controls = node->CountNumControls();
}
Expression::~Expression()
{
delete node;
}
static ParseStatus ParseExpressionInner(const std::string& str, ControlFinder& finder,
Expression** expr_out)
{
ParseStatus status;
Expression* expr;
*expr_out = nullptr;
if (str == "")
return ParseStatus::Successful;
Lexer l(str); Lexer l(str);
std::vector<Token> tokens; std::vector<Token> tokens;
status = l.Tokenize(tokens); ParseStatus tokenize_status = l.Tokenize(tokens);
if (status != ParseStatus::Successful) if (tokenize_status != ParseStatus::Successful)
return status; return {tokenize_status};
Parser p(tokens, finder); return Parser(std::move(tokens)).Parse();
status = p.Parse(&expr);
if (status != ParseStatus::Successful)
return status;
*expr_out = expr;
return ParseStatus::Successful;
} }
ParseStatus ParseExpression(const std::string& str, ControlFinder& finder, Expression** expr_out) static std::unique_ptr<Expression> ParseBarewordExpression(const std::string& str)
{ {
// Add compatibility with old simple expressions, which are simple
// barewords control names.
ControlQualifier qualifier; ControlQualifier qualifier;
qualifier.control_name = str; qualifier.control_name = str;
qualifier.has_device = false; qualifier.has_device = false;
std::shared_ptr<Device> device = finder.FindDevice(qualifier); return std::make_unique<ControlExpression>(qualifier);
Device::Control* control = finder.FindControl(qualifier); }
if (control)
std::pair<ParseStatus, std::unique_ptr<Expression>> ParseExpression(const std::string& str)
{
if (StripSpaces(str).empty())
return std::make_pair(ParseStatus::EmptyExpression, nullptr);
auto bareword_expr = ParseBarewordExpression(str);
ParseResult complex_result = ParseComplexExpression(str);
if (complex_result.status != ParseStatus::Successful)
{ {
*expr_out = new Expression(new ControlExpression(qualifier, device, control)); return std::make_pair(complex_result.status, std::move(bareword_expr));
return ParseStatus::Successful;
} }
return ParseExpressionInner(str, finder, expr_out); 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));
} }
} }
} }

View File

@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility>
#include "InputCommon/ControllerInterface/Device.h" #include "InputCommon/ControllerInterface/Device.h"
namespace ciface namespace ciface
@ -46,26 +47,24 @@ private:
bool is_input; bool is_input;
}; };
class ExpressionNode;
class Expression class Expression
{ {
public: public:
Expression() : node(nullptr) {} virtual ~Expression() = default;
Expression(ExpressionNode* node); virtual ControlState GetValue() const = 0;
~Expression(); virtual void SetValue(ControlState state) = 0;
ControlState GetValue() const; virtual int CountNumControls() const = 0;
void SetValue(ControlState state); virtual void UpdateReferences(ControlFinder& finder) = 0;
int num_controls; virtual operator std::string() const = 0;
ExpressionNode* node;
}; };
enum class ParseStatus enum class ParseStatus
{ {
Successful, Successful,
SyntaxError, SyntaxError,
NoDevice, EmptyExpression,
}; };
ParseStatus ParseExpression(const std::string& expr, ControlFinder& finder, Expression** expr_out); std::pair<ParseStatus, std::unique_ptr<Expression>> ParseExpression(const std::string& expr);
} }
} }

View File

@ -54,8 +54,12 @@ void ControlGroup::LoadConfig(IniFile::Section* sec, const std::string& defdev,
for (auto& c : controls) for (auto& c : controls)
{ {
// control expression {
sec->Get(group + c->name, &c->control_ref->expression, ""); // control expression
std::string expression;
sec->Get(group + c->name, &expression, "");
c->control_ref->SetExpression(std::move(expression));
}
// range // range
sec->Get(group + c->name + "/Range", &c->control_ref->range, 100.0); sec->Get(group + c->name + "/Range", &c->control_ref->range, 100.0);
@ -109,7 +113,7 @@ void ControlGroup::SaveConfig(IniFile::Section* sec, const std::string& defdev,
for (auto& c : controls) for (auto& c : controls)
{ {
// control expression // control expression
sec->Set(group + c->name, c->control_ref->expression, ""); sec->Set(group + c->name, c->control_ref->GetExpression(), "");
// range // range
sec->Set(group + c->name + "/Range", c->control_ref->range * 100.0, 100.0); sec->Set(group + c->name + "/Range", c->control_ref->range * 100.0, 100.0);
@ -128,6 +132,6 @@ void ControlGroup::SaveConfig(IniFile::Section* sec, const std::string& defdev,
void ControlGroup::SetControlExpression(int index, const std::string& expression) void ControlGroup::SetControlExpression(int index, const std::string& expression)
{ {
controls.at(index)->control_ref->expression = expression; controls.at(index)->control_ref->SetExpression(expression);
} }
} // namespace ControllerEmu } // namespace ControllerEmu