ExpressionParser: Renamed ControlFinder to ControlEnvironment. Added support for variables and assignment operator. ControlExpression objects now reference a matching input and output so the two can me mixed in any expression. (you can set rumble directly from inputs)

This commit is contained in:
Jordan Woyak 2018-12-30 16:06:29 -06:00
parent 1efcf861ea
commit e896835f86
7 changed files with 144 additions and 46 deletions

View File

@ -100,7 +100,7 @@ void MappingButton::Detect()
return; return;
m_reference->SetExpression(expression.toStdString()); m_reference->SetExpression(expression.toStdString());
m_parent->GetController()->UpdateReferences(g_controller_interface); m_parent->GetController()->UpdateSingleControlReference(g_controller_interface, m_reference);
ConfigChanged(); ConfigChanged();
m_parent->SaveSettings(); m_parent->SaveSettings();
@ -111,7 +111,7 @@ void MappingButton::Clear()
m_reference->range = 100.0 / SLIDER_TICK_COUNT; m_reference->range = 100.0 / SLIDER_TICK_COUNT;
m_reference->SetExpression(""); m_reference->SetExpression("");
m_parent->GetController()->UpdateReferences(g_controller_interface); m_parent->GetController()->UpdateSingleControlReference(g_controller_interface, m_reference);
m_parent->SaveSettings(); m_parent->SaveSettings();
ConfigChanged(); ConfigChanged();

View File

@ -25,12 +25,12 @@ bool ControlReference::InputGateOn()
// Updates a controlreference's binded devices/controls // Updates a controlreference's binded devices/controls
// need to call this to re-bind a control reference after changing its expression // 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(ciface::ExpressionParser::ControlEnvironment& env)
const ciface::Core::DeviceQualifier& default_device)
{ {
ControlFinder finder(devices, default_device, IsInput());
if (m_parsed_expression) if (m_parsed_expression)
m_parsed_expression->UpdateReferences(finder); {
m_parsed_expression->UpdateReferences(env);
}
} }
int ControlReference::BoundCount() const int ControlReference::BoundCount() const

View File

@ -30,8 +30,7 @@ public:
int BoundCount() const; int BoundCount() const;
ciface::ExpressionParser::ParseStatus GetParseStatus() const; ciface::ExpressionParser::ParseStatus GetParseStatus() const;
void UpdateReference(const ciface::Core::DeviceContainer& devices, void UpdateReference(ciface::ExpressionParser::ControlEnvironment& env);
const ciface::Core::DeviceQualifier& default_device);
std::string GetExpression() const; std::string GetExpression() const;
void SetExpression(std::string expr); void SetExpression(std::string expr);

View File

@ -35,8 +35,10 @@ enum TokenType
TOK_MUL, TOK_MUL,
TOK_DIV, TOK_DIV,
TOK_MOD, TOK_MOD,
TOK_ASSIGN,
TOK_CONTROL, TOK_CONTROL,
TOK_LITERAL, TOK_LITERAL,
TOK_VARIABLE,
}; };
inline std::string OpName(TokenType op) inline std::string OpName(TokenType op)
@ -57,6 +59,10 @@ inline std::string OpName(TokenType op)
return "Div"; return "Div";
case TOK_MOD: case TOK_MOD:
return "Mod"; return "Mod";
case TOK_ASSIGN:
return "Assign";
case TOK_VARIABLE:
return "Var";
default: default:
assert(false); assert(false);
return ""; return "";
@ -97,10 +103,14 @@ public:
return "/"; return "/";
case TOK_MOD: case TOK_MOD:
return "%"; return "%";
case TOK_ASSIGN:
return "=";
case TOK_CONTROL: case TOK_CONTROL:
return "Device(" + data + ")"; return "Device(" + data + ")";
case TOK_LITERAL: case TOK_LITERAL:
return '\'' + data + '\''; return '\'' + data + '\'';
case TOK_VARIABLE:
return '$' + data;
case TOK_INVALID: case TOK_INVALID:
break; break;
} }
@ -131,21 +141,23 @@ public:
return false; return false;
} }
Token GetUnaryFunction() std::string FetchWordChars()
{ {
std::string name; std::string word;
std::regex valid_name_char("[a-z0-9_]", std::regex_constants::icase); std::regex valid_name_char("[a-z0-9_]", std::regex_constants::icase);
while (it != expr.end() && std::regex_match(std::string(1, *it), valid_name_char)) while (it != expr.end() && std::regex_match(std::string(1, *it), valid_name_char))
{ {
name += *it; word += *it;
++it; ++it;
} }
return Token(TOK_UNARY, name); return word;
} }
Token GetUnaryFunction() { return Token(TOK_UNARY, FetchWordChars()); }
Token GetLiteral() Token GetLiteral()
{ {
std::string value; std::string value;
@ -153,6 +165,8 @@ public:
return Token(TOK_LITERAL, value); return Token(TOK_LITERAL, value);
} }
Token GetVariable() { return Token(TOK_VARIABLE, FetchWordChars()); }
Token GetFullyQualifiedControl() Token GetFullyQualifiedControl()
{ {
std::string value; std::string value;
@ -210,8 +224,12 @@ public:
return Token(TOK_DIV); return Token(TOK_DIV);
case '%': case '%':
return Token(TOK_MOD); return Token(TOK_MOD);
case '=':
return Token(TOK_ASSIGN);
case '\'': case '\'':
return GetLiteral(); return GetLiteral();
case '$':
return GetVariable();
case '`': case '`':
return GetFullyQualifiedControl(); return GetFullyQualifiedControl();
default: default:
@ -249,15 +267,14 @@ public:
class ControlExpression : public Expression class ControlExpression : public Expression
{ {
public: public:
ControlQualifier qualifier;
Device::Control* control = nullptr;
// Keep a shared_ptr to the device so the control pointer doesn't become invalid // 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:
std::shared_ptr<Device> m_device; std::shared_ptr<Device> m_device;
explicit ControlExpression(ControlQualifier qualifier_) : qualifier(qualifier_) {} explicit ControlExpression(ControlQualifier qualifier_) : qualifier(qualifier_) {}
ControlState GetValue() const override ControlState GetValue() const override
{ {
if (!control) if (!input)
return 0.0; return 0.0;
// Note: Inputs may return negative values in situations where opposing directions are // Note: Inputs may return negative values in situations where opposing directions are
@ -266,20 +283,26 @@ public:
// FYI: Clamping values greater than 1.0 is purposely not done to support unbounded values in // FYI: Clamping values greater than 1.0 is purposely not done to support unbounded values in
// the future. (e.g. raw accelerometer/gyro data) // the future. (e.g. raw accelerometer/gyro data)
return std::max(0.0, control->ToInput()->GetState()); return std::max(0.0, input->GetState());
} }
void SetValue(ControlState value) override void SetValue(ControlState value) override
{ {
if (control) if (output)
control->ToOutput()->SetState(value); output->SetState(value);
} }
int CountNumControls() const override { return control ? 1 : 0; } int CountNumControls() const override { return (input || output) ? 1 : 0; }
void UpdateReferences(ControlFinder& finder) override void UpdateReferences(ControlEnvironment& env) override
{ {
m_device = finder.FindDevice(qualifier); m_device = env.FindDevice(qualifier);
control = finder.FindControl(qualifier); input = env.FindInput(qualifier);
output = env.FindOutput(qualifier);
} }
operator std::string() const override { return "`" + static_cast<std::string>(qualifier) + "`"; } operator std::string() const override { return "`" + static_cast<std::string>(qualifier) + "`"; }
private:
ControlQualifier qualifier;
Device::Input* input = nullptr;
Device::Output* output = nullptr;
}; };
class BinaryExpression : public Expression class BinaryExpression : public Expression
@ -319,6 +342,12 @@ public:
const ControlState result = std::fmod(lhsValue, rhsValue); const ControlState result = std::fmod(lhsValue, rhsValue);
return std::isnan(result) ? 0.0 : result; return std::isnan(result) ? 0.0 : result;
} }
case TOK_ASSIGN:
{
lhs->SetValue(rhsValue);
// TODO: Should this instead GetValue(lhs) ?
return rhsValue;
}
default: default:
assert(false); assert(false);
return 0; return 0;
@ -338,10 +367,10 @@ public:
return lhs->CountNumControls() + rhs->CountNumControls(); return lhs->CountNumControls() + rhs->CountNumControls();
} }
void UpdateReferences(ControlFinder& finder) override void UpdateReferences(ControlEnvironment& env) override
{ {
lhs->UpdateReferences(finder); lhs->UpdateReferences(env);
rhs->UpdateReferences(finder); rhs->UpdateReferences(env);
} }
operator std::string() const override operator std::string() const override
@ -356,7 +385,7 @@ public:
UnaryExpression(std::unique_ptr<Expression>&& inner_) : inner(std::move(inner_)) {} UnaryExpression(std::unique_ptr<Expression>&& inner_) : inner(std::move(inner_)) {}
int CountNumControls() const override { return inner->CountNumControls(); } int CountNumControls() const override { return inner->CountNumControls(); }
void UpdateReferences(ControlFinder& finder) override { inner->UpdateReferences(finder); } void UpdateReferences(ControlEnvironment& env) override { inner->UpdateReferences(env); }
operator std::string() const override operator std::string() const override
{ {
@ -462,7 +491,7 @@ public:
int CountNumControls() const override { return 1; } int CountNumControls() const override { return 1; }
void UpdateReferences(ControlFinder&) override void UpdateReferences(ControlEnvironment&) override
{ {
// Nothing needed. // Nothing needed.
} }
@ -523,6 +552,29 @@ std::unique_ptr<LiteralExpression> MakeLiteralExpression(std::string name)
} }
} }
class VariableExpression : public Expression
{
public:
VariableExpression(std::string name) : m_name(name) {}
ControlState GetValue() const override { return *m_value_ptr; }
void SetValue(ControlState value) override { *m_value_ptr = value; }
int CountNumControls() const override { return 1; }
void UpdateReferences(ControlEnvironment& env) override
{
m_value_ptr = env.GetVariablePtr(m_name);
}
operator std::string() const override { return '$' + m_name; }
protected:
const std::string m_name;
ControlState* m_value_ptr{};
};
// This class proxies all methods to its either left-hand child if it has bound controls, or its // 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. // right-hand child. Its intended use is for supporting old-style barewords expressions.
class CoalesceExpression : public Expression class CoalesceExpression : public Expression
@ -543,10 +595,10 @@ public:
static_cast<std::string>(*m_rhs) + ')'; static_cast<std::string>(*m_rhs) + ')';
} }
void UpdateReferences(ControlFinder& finder) override void UpdateReferences(ControlEnvironment& env) override
{ {
m_lhs->UpdateReferences(finder); m_lhs->UpdateReferences(env);
m_rhs->UpdateReferences(finder); m_rhs->UpdateReferences(env);
} }
private: private:
@ -559,7 +611,7 @@ private:
std::unique_ptr<Expression> m_rhs; std::unique_ptr<Expression> m_rhs;
}; };
std::shared_ptr<Device> ControlFinder::FindDevice(ControlQualifier qualifier) const std::shared_ptr<Device> ControlEnvironment::FindDevice(ControlQualifier qualifier) const
{ {
if (qualifier.has_device) if (qualifier.has_device)
return container.FindDevice(qualifier.device_qualifier); return container.FindDevice(qualifier.device_qualifier);
@ -567,16 +619,27 @@ std::shared_ptr<Device> ControlFinder::FindDevice(ControlQualifier qualifier) co
return container.FindDevice(default_device); return container.FindDevice(default_device);
} }
Device::Control* ControlFinder::FindControl(ControlQualifier qualifier) const Device::Input* ControlEnvironment::FindInput(ControlQualifier qualifier) const
{ {
const std::shared_ptr<Device> device = FindDevice(qualifier); const std::shared_ptr<Device> device = FindDevice(qualifier);
if (!device) if (!device)
return nullptr; return nullptr;
if (is_input) return device->FindInput(qualifier.control_name);
return device->FindInput(qualifier.control_name); }
else
return device->FindOutput(qualifier.control_name); Device::Output* ControlEnvironment::FindOutput(ControlQualifier qualifier) const
{
const std::shared_ptr<Device> device = FindDevice(qualifier);
if (!device)
return nullptr;
return device->FindOutput(qualifier.control_name);
}
ControlState* ControlEnvironment::GetVariablePtr(const std::string& name)
{
return &m_variables[name];
} }
struct ParseResult struct ParseResult
@ -623,6 +686,10 @@ private:
{ {
return {ParseStatus::Successful, MakeLiteralExpression(tok.data)}; return {ParseStatus::Successful, MakeLiteralExpression(tok.data)};
} }
case TOK_VARIABLE:
{
return {ParseStatus::Successful, std::make_unique<VariableExpression>(tok.data)};
}
case TOK_LPAREN: case TOK_LPAREN:
return Paren(); return Paren();
default: default:
@ -665,6 +732,7 @@ private:
case TOK_MUL: case TOK_MUL:
case TOK_DIV: case TOK_DIV:
case TOK_MOD: case TOK_MOD:
case TOK_ASSIGN:
return true; return true;
default: default:
return false; return false;

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility> #include <utility>
@ -46,21 +47,26 @@ public:
} }
}; };
class ControlFinder class ControlEnvironment
{ {
public: public:
ControlFinder(const Core::DeviceContainer& container_, const Core::DeviceQualifier& default_, using VariableContainer = std::map<std::string, ControlState>;
const bool is_input_)
: container(container_), default_device(default_), is_input(is_input_) ControlEnvironment(const Core::DeviceContainer& container_, const Core::DeviceQualifier& default_,
VariableContainer& vars)
: m_variables(vars), container(container_), default_device(default_)
{ {
} }
std::shared_ptr<Core::Device> FindDevice(ControlQualifier qualifier) const; std::shared_ptr<Core::Device> FindDevice(ControlQualifier qualifier) const;
Core::Device::Control* FindControl(ControlQualifier qualifier) const; Core::Device::Input* FindInput(ControlQualifier qualifier) const;
Core::Device::Output* FindOutput(ControlQualifier qualifier) const;
ControlState* GetVariablePtr(const std::string& name);
private: private:
VariableContainer& m_variables;
const Core::DeviceContainer& container; const Core::DeviceContainer& container;
const Core::DeviceQualifier& default_device; const Core::DeviceQualifier& default_device;
bool is_input;
}; };
class Expression class Expression
@ -70,7 +76,7 @@ public:
virtual ControlState GetValue() const = 0; virtual ControlState GetValue() const = 0;
virtual void SetValue(ControlState state) = 0; virtual void SetValue(ControlState state) = 0;
virtual int CountNumControls() const = 0; virtual int CountNumControls() const = 0;
virtual void UpdateReferences(ControlFinder& finder) = 0; virtual void UpdateReferences(ControlEnvironment& finder) = 0;
virtual operator std::string() const = 0; virtual operator std::string() const = 0;
}; };

View File

@ -38,23 +38,38 @@ std::unique_lock<std::recursive_mutex> EmulatedController::GetStateLock()
void EmulatedController::UpdateReferences(const ControllerInterface& devi) void EmulatedController::UpdateReferences(const ControllerInterface& devi)
{ {
const auto lock = GetStateLock();
m_default_device_is_connected = devi.HasConnectedDevice(m_default_device); m_default_device_is_connected = devi.HasConnectedDevice(m_default_device);
ciface::ExpressionParser::ControlEnvironment env(devi, GetDefaultDevice(), m_expression_vars);
UpdateReferences(env);
}
void EmulatedController::UpdateReferences(ciface::ExpressionParser::ControlEnvironment& env)
{
const auto lock = GetStateLock();
for (auto& ctrlGroup : groups) for (auto& ctrlGroup : groups)
{ {
for (auto& control : ctrlGroup->controls) for (auto& control : ctrlGroup->controls)
control->control_ref.get()->UpdateReference(devi, GetDefaultDevice()); control->control_ref->UpdateReference(env);
// Attachments: // Attachments:
if (ctrlGroup->type == GroupType::Attachments) if (ctrlGroup->type == GroupType::Attachments)
{ {
for (auto& attachment : static_cast<Attachments*>(ctrlGroup.get())->GetAttachmentList()) for (auto& attachment : static_cast<Attachments*>(ctrlGroup.get())->GetAttachmentList())
attachment->UpdateReferences(devi); attachment->UpdateReferences(env);
} }
} }
} }
void EmulatedController::UpdateSingleControlReference(const ControllerInterface& devi,
ControlReference* ref)
{
ciface::ExpressionParser::ControlEnvironment env(devi, GetDefaultDevice(), m_expression_vars);
ref->UpdateReference(env);
}
bool EmulatedController::IsDefaultDeviceConnected() const bool EmulatedController::IsDefaultDeviceConnected() const
{ {
return m_default_device_is_connected; return m_default_device_is_connected;

View File

@ -13,6 +13,7 @@
#include "Common/Common.h" #include "Common/Common.h"
#include "Common/IniFile.h" #include "Common/IniFile.h"
#include "InputCommon/ControlReference/ExpressionParser.h"
#include "InputCommon/ControllerInterface/Device.h" #include "InputCommon/ControllerInterface/Device.h"
class ControllerInterface; class ControllerInterface;
@ -20,6 +21,8 @@ class ControllerInterface;
const char* const named_directions[] = {_trans("Up"), _trans("Down"), _trans("Left"), const char* const named_directions[] = {_trans("Up"), _trans("Down"), _trans("Left"),
_trans("Right")}; _trans("Right")};
class ControlReference;
namespace ControllerEmu namespace ControllerEmu
{ {
class ControlGroup; class ControlGroup;
@ -43,6 +46,7 @@ public:
void SetDefaultDevice(ciface::Core::DeviceQualifier devq); void SetDefaultDevice(ciface::Core::DeviceQualifier devq);
void UpdateReferences(const ControllerInterface& devi); void UpdateReferences(const ControllerInterface& devi);
void UpdateSingleControlReference(const ControllerInterface& devi, ControlReference* ref);
// This returns a lock that should be held before calling State() on any control // This returns a lock that should be held before calling State() on any control
// references and GetState(), by extension. This prevents a race condition // references and GetState(), by extension. This prevents a race condition
@ -75,6 +79,12 @@ public:
return T(std::lround((zero_value - neg_1_value) * input_value + zero_value)); return T(std::lround((zero_value - neg_1_value) * input_value + zero_value));
} }
protected:
// TODO: Wiimote attachment has its own member that isn't being used..
ciface::ExpressionParser::ControlEnvironment::VariableContainer m_expression_vars;
void UpdateReferences(ciface::ExpressionParser::ControlEnvironment& env);
private: private:
ciface::Core::DeviceQualifier m_default_device; ciface::Core::DeviceQualifier m_default_device;
bool m_default_device_is_connected{false}; bool m_default_device_is_connected{false};