mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-21 05:09:34 -06:00
InputCommon: Extract ControlReference from ControllerInterface
Better separation of concerns. Relegates `ControllerInterface` to enumerating input controls, and the new `ControlReference` deals with combining inputs and configuration expression parsing.
This commit is contained in:
148
Source/Core/InputCommon/ControlReference/ControlReference.cpp
Normal file
148
Source/Core/InputCommon/ControlReference/ControlReference.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Common/Thread.h"
|
||||
// For InputGateOn()
|
||||
// This is a bad layering violation, but it's the cleanest
|
||||
// place I could find to put it.
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Host.h"
|
||||
|
||||
#include "InputCommon/ControlReference/ControlReference.h"
|
||||
|
||||
using namespace ciface::ExpressionParser;
|
||||
|
||||
constexpr ControlState INPUT_DETECT_THRESHOLD = 0.55;
|
||||
|
||||
bool ControlReference::InputGateOn()
|
||||
{
|
||||
return SConfig::GetInstance().m_BackgroundInput || Host_RendererHasFocus() || Host_UIHasFocus();
|
||||
}
|
||||
|
||||
//
|
||||
// UpdateReference
|
||||
//
|
||||
// Updates a controlreference's binded devices/controls
|
||||
// need to call this to re-parse a control reference's expression after changing it
|
||||
//
|
||||
void ControlReference::UpdateReference(ciface::Core::DeviceContainer& devices,
|
||||
const ciface::Core::DeviceQualifier& default_device)
|
||||
{
|
||||
delete parsed_expression;
|
||||
parsed_expression = nullptr;
|
||||
|
||||
ControlFinder finder(devices, default_device, is_input);
|
||||
parse_error = ParseExpression(expression, finder, &parsed_expression);
|
||||
}
|
||||
|
||||
//
|
||||
// InputReference :: State
|
||||
//
|
||||
// Gets the state of an input reference
|
||||
// override function for ControlReference::State ...
|
||||
//
|
||||
ControlState InputReference::State(const ControlState ignore)
|
||||
{
|
||||
if (parsed_expression && InputGateOn())
|
||||
return parsed_expression->GetValue() * range;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
//
|
||||
// OutputReference :: State
|
||||
//
|
||||
// Set the state of all binded outputs
|
||||
// overrides ControlReference::State .. combined them so I could make the GUI simple / inputs ==
|
||||
// same as outputs one list
|
||||
// I was lazy and it works so watever
|
||||
//
|
||||
ControlState OutputReference::State(const ControlState state)
|
||||
{
|
||||
if (parsed_expression && InputGateOn())
|
||||
parsed_expression->SetValue(state);
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
//
|
||||
// InputReference :: Detect
|
||||
//
|
||||
// Wait for input on all binded devices
|
||||
// supports not detecting inputs that were held down at the time of Detect start,
|
||||
// which is useful for those crazy flightsticks that have certain buttons that are always held down
|
||||
// or some crazy axes or something
|
||||
// upon input, return pointer to detected Control
|
||||
// else return nullptr
|
||||
//
|
||||
ciface::Core::Device::Control* InputReference::Detect(const unsigned int ms,
|
||||
ciface::Core::Device* const device)
|
||||
{
|
||||
unsigned int time = 0;
|
||||
std::vector<bool> states(device->Inputs().size());
|
||||
|
||||
if (device->Inputs().size() == 0)
|
||||
return nullptr;
|
||||
|
||||
// get starting state of all inputs,
|
||||
// so we can ignore those that were activated at time of Detect start
|
||||
std::vector<ciface::Core::Device::Input *>::const_iterator i = device->Inputs().begin(),
|
||||
e = device->Inputs().end();
|
||||
for (std::vector<bool>::iterator state = states.begin(); i != e; ++i)
|
||||
*state++ = ((*i)->GetState() > (1 - INPUT_DETECT_THRESHOLD));
|
||||
|
||||
while (time < ms)
|
||||
{
|
||||
device->UpdateInput();
|
||||
i = device->Inputs().begin();
|
||||
for (std::vector<bool>::iterator state = states.begin(); i != e; ++i, ++state)
|
||||
{
|
||||
// detected an input
|
||||
if ((*i)->IsDetectable() && (*i)->GetState() > INPUT_DETECT_THRESHOLD)
|
||||
{
|
||||
// input was released at some point during Detect call
|
||||
// return the detected input
|
||||
if (false == *state)
|
||||
return *i;
|
||||
}
|
||||
else if ((*i)->GetState() < (1 - INPUT_DETECT_THRESHOLD))
|
||||
{
|
||||
*state = false;
|
||||
}
|
||||
}
|
||||
Common::SleepCurrentThread(10);
|
||||
time += 10;
|
||||
}
|
||||
|
||||
// no input was detected
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//
|
||||
// OutputReference :: Detect
|
||||
//
|
||||
// Totally different from the inputReference detect / I have them combined so it was simpler to make
|
||||
// the GUI.
|
||||
// The GUI doesn't know the difference between an input and an output / it's odd but I was lazy and
|
||||
// it was easy
|
||||
//
|
||||
// set all binded outputs to <range> power for x milliseconds return false
|
||||
//
|
||||
ciface::Core::Device::Control* OutputReference::Detect(const unsigned int ms,
|
||||
ciface::Core::Device* const device)
|
||||
{
|
||||
// ignore device
|
||||
|
||||
// don't hang if we don't even have any controls mapped
|
||||
if (BoundCount() > 0)
|
||||
{
|
||||
State(1);
|
||||
unsigned int slept = 0;
|
||||
|
||||
// this loop is to make stuff like flashing keyboard LEDs work
|
||||
while (ms > (slept += 10))
|
||||
Common::SleepCurrentThread(10);
|
||||
|
||||
State(0);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
79
Source/Core/InputCommon/ControlReference/ControlReference.h
Normal file
79
Source/Core/InputCommon/ControlReference/ControlReference.h
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "InputCommon/ControlReference/ExpressionParser.h"
|
||||
#include "InputCommon/ControllerInterface/Device.h"
|
||||
|
||||
// ControlReference
|
||||
//
|
||||
// These are what you create to actually use the inputs, InputReference or OutputReference.
|
||||
//
|
||||
// After being bound to devices and controls with UpdateReference,
|
||||
// each one can link to multiple devices and controls
|
||||
// when you change a ControlReference's expression,
|
||||
// you must use UpdateReference on it to rebind controls
|
||||
//
|
||||
|
||||
class ControlReference
|
||||
{
|
||||
public:
|
||||
static bool InputGateOn();
|
||||
|
||||
virtual ControlState State(const ControlState state = 0) = 0;
|
||||
virtual ciface::Core::Device::Control* Detect(const unsigned int ms,
|
||||
ciface::Core::Device* const device) = 0;
|
||||
|
||||
void UpdateReference(ciface::Core::DeviceContainer& devices,
|
||||
const ciface::Core::DeviceQualifier& default_device);
|
||||
|
||||
ControlState range;
|
||||
std::string expression;
|
||||
const bool is_input;
|
||||
ciface::ExpressionParser::ExpressionParseStatus parse_error;
|
||||
|
||||
virtual ~ControlReference() { delete parsed_expression; }
|
||||
int BoundCount()
|
||||
{
|
||||
if (parsed_expression)
|
||||
return parsed_expression->num_controls;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
ControlReference(const bool _is_input) : range(1), is_input(_is_input), parsed_expression(nullptr)
|
||||
{
|
||||
}
|
||||
ciface::ExpressionParser::Expression* parsed_expression;
|
||||
};
|
||||
|
||||
//
|
||||
// InputReference
|
||||
//
|
||||
// Control reference for inputs
|
||||
//
|
||||
class InputReference : public ControlReference
|
||||
{
|
||||
public:
|
||||
InputReference() : ControlReference(true) {}
|
||||
ControlState State(const ControlState state) override;
|
||||
ciface::Core::Device::Control* Detect(const unsigned int ms,
|
||||
ciface::Core::Device* const device) override;
|
||||
};
|
||||
|
||||
//
|
||||
// OutputReference
|
||||
//
|
||||
// Control reference for outputs
|
||||
//
|
||||
class OutputReference : public ControlReference
|
||||
{
|
||||
public:
|
||||
OutputReference() : ControlReference(false) {}
|
||||
ControlState State(const ControlState state) override;
|
||||
ciface::Core::Device::Control* Detect(const unsigned int ms,
|
||||
ciface::Core::Device* const device) override;
|
||||
};
|
567
Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
Normal file
567
Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
Normal file
@ -0,0 +1,567 @@
|
||||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "InputCommon/ControlReference/ExpressionParser.h"
|
||||
|
||||
using namespace ciface::Core;
|
||||
|
||||
namespace ciface
|
||||
{
|
||||
namespace ExpressionParser
|
||||
{
|
||||
enum TokenType
|
||||
{
|
||||
TOK_DISCARD,
|
||||
TOK_INVALID,
|
||||
TOK_EOF,
|
||||
TOK_LPAREN,
|
||||
TOK_RPAREN,
|
||||
TOK_AND,
|
||||
TOK_OR,
|
||||
TOK_NOT,
|
||||
TOK_ADD,
|
||||
TOK_CONTROL,
|
||||
};
|
||||
|
||||
inline std::string OpName(TokenType op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case TOK_AND:
|
||||
return "And";
|
||||
case TOK_OR:
|
||||
return "Or";
|
||||
case TOK_NOT:
|
||||
return "Not";
|
||||
case TOK_ADD:
|
||||
return "Add";
|
||||
default:
|
||||
assert(false);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
class Token
|
||||
{
|
||||
public:
|
||||
TokenType type;
|
||||
ControlQualifier qualifier;
|
||||
|
||||
Token(TokenType type_) : type(type_) {}
|
||||
Token(TokenType type_, ControlQualifier qualifier_) : type(type_), qualifier(qualifier_) {}
|
||||
operator std::string()
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TOK_DISCARD:
|
||||
return "Discard";
|
||||
case TOK_EOF:
|
||||
return "EOF";
|
||||
case TOK_LPAREN:
|
||||
return "(";
|
||||
case TOK_RPAREN:
|
||||
return ")";
|
||||
case TOK_AND:
|
||||
return "&";
|
||||
case TOK_OR:
|
||||
return "|";
|
||||
case TOK_NOT:
|
||||
return "!";
|
||||
case TOK_ADD:
|
||||
return "+";
|
||||
case TOK_CONTROL:
|
||||
return "Device(" + (std::string)qualifier + ")";
|
||||
case TOK_INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
return "Invalid";
|
||||
}
|
||||
};
|
||||
|
||||
class Lexer
|
||||
{
|
||||
public:
|
||||
std::string expr;
|
||||
std::string::iterator it;
|
||||
|
||||
Lexer(const std::string& expr_) : expr(expr_) { it = expr.begin(); }
|
||||
bool FetchBacktickString(std::string& value, char otherDelim = 0)
|
||||
{
|
||||
value = "";
|
||||
while (it != expr.end())
|
||||
{
|
||||
char c = *it;
|
||||
++it;
|
||||
if (c == '`')
|
||||
return false;
|
||||
if (c > 0 && c == otherDelim)
|
||||
return true;
|
||||
value += c;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Token GetFullyQualifiedControl()
|
||||
{
|
||||
ControlQualifier qualifier;
|
||||
std::string value;
|
||||
|
||||
if (FetchBacktickString(value, ':'))
|
||||
{
|
||||
// Found colon, this is the device name
|
||||
qualifier.has_device = true;
|
||||
qualifier.device_qualifier.FromString(value);
|
||||
FetchBacktickString(value);
|
||||
}
|
||||
|
||||
qualifier.control_name = value;
|
||||
|
||||
return Token(TOK_CONTROL, qualifier);
|
||||
}
|
||||
|
||||
Token GetBarewordsControl(char c)
|
||||
{
|
||||
std::string name;
|
||||
name += c;
|
||||
|
||||
while (it != expr.end())
|
||||
{
|
||||
c = *it;
|
||||
if (!isalpha(c))
|
||||
break;
|
||||
name += c;
|
||||
++it;
|
||||
}
|
||||
|
||||
ControlQualifier qualifier;
|
||||
qualifier.control_name = name;
|
||||
return Token(TOK_CONTROL, qualifier);
|
||||
}
|
||||
|
||||
Token NextToken()
|
||||
{
|
||||
if (it == expr.end())
|
||||
return Token(TOK_EOF);
|
||||
|
||||
char c = *it++;
|
||||
switch (c)
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
return Token(TOK_DISCARD);
|
||||
case '(':
|
||||
return Token(TOK_LPAREN);
|
||||
case ')':
|
||||
return Token(TOK_RPAREN);
|
||||
case '&':
|
||||
return Token(TOK_AND);
|
||||
case '|':
|
||||
return Token(TOK_OR);
|
||||
case '!':
|
||||
return Token(TOK_NOT);
|
||||
case '+':
|
||||
return Token(TOK_ADD);
|
||||
case '`':
|
||||
return GetFullyQualifiedControl();
|
||||
default:
|
||||
if (isalpha(c))
|
||||
return GetBarewordsControl(c);
|
||||
else
|
||||
return Token(TOK_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionParseStatus Tokenize(std::vector<Token>& tokens)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Token tok = NextToken();
|
||||
|
||||
if (tok.type == TOK_DISCARD)
|
||||
continue;
|
||||
|
||||
if (tok.type == TOK_INVALID)
|
||||
{
|
||||
tokens.clear();
|
||||
return EXPRESSION_PARSE_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
tokens.push_back(tok);
|
||||
|
||||
if (tok.type == TOK_EOF)
|
||||
break;
|
||||
}
|
||||
return EXPRESSION_PARSE_SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
class ExpressionNode
|
||||
{
|
||||
public:
|
||||
virtual ~ExpressionNode() {}
|
||||
virtual ControlState GetValue() { return 0; }
|
||||
virtual void SetValue(ControlState state) {}
|
||||
virtual int CountNumControls() { return 0; }
|
||||
virtual operator std::string() { return ""; }
|
||||
};
|
||||
|
||||
class DummyExpression : public ExpressionNode
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
DummyExpression(const std::string& name_) : name(name_) {}
|
||||
ControlState GetValue() override { return 0.0; }
|
||||
void SetValue(ControlState value) override {}
|
||||
int CountNumControls() override { return 0; }
|
||||
operator std::string() override { return "`" + name + "`"; }
|
||||
};
|
||||
|
||||
class ControlExpression : public ExpressionNode
|
||||
{
|
||||
public:
|
||||
ControlQualifier qualifier;
|
||||
Device::Control* control;
|
||||
|
||||
ControlExpression(ControlQualifier qualifier_, std::shared_ptr<Device> device,
|
||||
Device::Control* control_)
|
||||
: qualifier(qualifier_), control(control_), m_device(device)
|
||||
{
|
||||
}
|
||||
|
||||
ControlState GetValue() override { return control->ToInput()->GetState(); }
|
||||
void SetValue(ControlState value) override { control->ToOutput()->SetState(value); }
|
||||
int CountNumControls() override { return 1; }
|
||||
operator std::string() override { return "`" + (std::string)qualifier + "`"; }
|
||||
private:
|
||||
std::shared_ptr<Device> m_device;
|
||||
};
|
||||
|
||||
class BinaryExpression : public ExpressionNode
|
||||
{
|
||||
public:
|
||||
TokenType op;
|
||||
ExpressionNode* lhs;
|
||||
ExpressionNode* rhs;
|
||||
|
||||
BinaryExpression(TokenType op_, ExpressionNode* lhs_, ExpressionNode* rhs_)
|
||||
: op(op_), lhs(lhs_), rhs(rhs_)
|
||||
{
|
||||
}
|
||||
virtual ~BinaryExpression()
|
||||
{
|
||||
delete lhs;
|
||||
delete rhs;
|
||||
}
|
||||
|
||||
ControlState GetValue() override
|
||||
{
|
||||
ControlState lhsValue = lhs->GetValue();
|
||||
ControlState rhsValue = rhs->GetValue();
|
||||
switch (op)
|
||||
{
|
||||
case TOK_AND:
|
||||
return std::min(lhsValue, rhsValue);
|
||||
case TOK_OR:
|
||||
return std::max(lhsValue, rhsValue);
|
||||
case TOK_ADD:
|
||||
return std::min(lhsValue + rhsValue, 1.0);
|
||||
default:
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SetValue(ControlState value) override
|
||||
{
|
||||
// Don't do anything special with the op we have.
|
||||
// Treat "A & B" the same as "A | B".
|
||||
lhs->SetValue(value);
|
||||
rhs->SetValue(value);
|
||||
}
|
||||
|
||||
int CountNumControls() override { return lhs->CountNumControls() + rhs->CountNumControls(); }
|
||||
operator std::string() override
|
||||
{
|
||||
return OpName(op) + "(" + (std::string)(*lhs) + ", " + (std::string)(*rhs) + ")";
|
||||
}
|
||||
};
|
||||
|
||||
class UnaryExpression : public ExpressionNode
|
||||
{
|
||||
public:
|
||||
TokenType op;
|
||||
ExpressionNode* inner;
|
||||
|
||||
UnaryExpression(TokenType op_, ExpressionNode* inner_) : op(op_), inner(inner_) {}
|
||||
virtual ~UnaryExpression() { delete inner; }
|
||||
ControlState GetValue() override
|
||||
{
|
||||
ControlState value = inner->GetValue();
|
||||
switch (op)
|
||||
{
|
||||
case TOK_NOT:
|
||||
return 1.0 - value;
|
||||
default:
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SetValue(ControlState value) override
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case TOK_NOT:
|
||||
inner->SetValue(1.0 - value);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
int CountNumControls() override { return inner->CountNumControls(); }
|
||||
operator std::string() override { return OpName(op) + "(" + (std::string)(*inner) + ")"; }
|
||||
};
|
||||
|
||||
std::shared_ptr<Device> ControlFinder::FindDevice(ControlQualifier qualifier)
|
||||
{
|
||||
if (qualifier.has_device)
|
||||
return container.FindDevice(qualifier.device_qualifier);
|
||||
else
|
||||
return container.FindDevice(default_device);
|
||||
}
|
||||
|
||||
Device::Control* ControlFinder::FindControl(ControlQualifier qualifier)
|
||||
{
|
||||
const std::shared_ptr<Device> device = FindDevice(qualifier);
|
||||
if (!device)
|
||||
return nullptr;
|
||||
|
||||
if (is_input)
|
||||
return device->FindInput(qualifier.control_name);
|
||||
else
|
||||
return device->FindOutput(qualifier.control_name);
|
||||
}
|
||||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
Parser(std::vector<Token> tokens_, ControlFinder& finder_) : tokens(tokens_), finder(finder_)
|
||||
{
|
||||
m_it = tokens.begin();
|
||||
}
|
||||
|
||||
ExpressionParseStatus Parse(Expression** expr_out)
|
||||
{
|
||||
ExpressionNode* node;
|
||||
ExpressionParseStatus status = Toplevel(&node);
|
||||
if (status != EXPRESSION_PARSE_SUCCESS)
|
||||
return status;
|
||||
|
||||
*expr_out = new Expression(node);
|
||||
return EXPRESSION_PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Token> tokens;
|
||||
std::vector<Token>::iterator m_it;
|
||||
ControlFinder& finder;
|
||||
|
||||
Token Chew() { return *m_it++; }
|
||||
Token Peek() { return *m_it; }
|
||||
bool Expects(TokenType type)
|
||||
{
|
||||
Token tok = Chew();
|
||||
return tok.type == type;
|
||||
}
|
||||
|
||||
ExpressionParseStatus Atom(ExpressionNode** expr_out)
|
||||
{
|
||||
Token tok = Chew();
|
||||
switch (tok.type)
|
||||
{
|
||||
case TOK_CONTROL:
|
||||
{
|
||||
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 EXPRESSION_PARSE_NO_DEVICE;
|
||||
}
|
||||
|
||||
*expr_out = new ControlExpression(tok.qualifier, device, control);
|
||||
return EXPRESSION_PARSE_SUCCESS;
|
||||
}
|
||||
case TOK_LPAREN:
|
||||
return Paren(expr_out);
|
||||
default:
|
||||
return EXPRESSION_PARSE_SYNTAX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsUnaryExpression(TokenType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TOK_NOT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionParseStatus Unary(ExpressionNode** expr_out)
|
||||
{
|
||||
if (IsUnaryExpression(Peek().type))
|
||||
{
|
||||
Token tok = Chew();
|
||||
ExpressionNode* atom_expr;
|
||||
ExpressionParseStatus status = Atom(&atom_expr);
|
||||
if (status == EXPRESSION_PARSE_SYNTAX_ERROR)
|
||||
return status;
|
||||
*expr_out = new UnaryExpression(tok.type, atom_expr);
|
||||
return EXPRESSION_PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
return Atom(expr_out);
|
||||
}
|
||||
|
||||
bool IsBinaryToken(TokenType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TOK_AND:
|
||||
case TOK_OR:
|
||||
case TOK_ADD:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionParseStatus Binary(ExpressionNode** expr_out)
|
||||
{
|
||||
ExpressionParseStatus status = Unary(expr_out);
|
||||
if (status == EXPRESSION_PARSE_SYNTAX_ERROR)
|
||||
return status;
|
||||
|
||||
while (IsBinaryToken(Peek().type))
|
||||
{
|
||||
Token tok = Chew();
|
||||
ExpressionNode* unary_expr;
|
||||
status = Unary(&unary_expr);
|
||||
if (status == EXPRESSION_PARSE_SYNTAX_ERROR)
|
||||
{
|
||||
delete *expr_out;
|
||||
return status;
|
||||
}
|
||||
|
||||
*expr_out = new BinaryExpression(tok.type, *expr_out, unary_expr);
|
||||
}
|
||||
|
||||
return EXPRESSION_PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
ExpressionParseStatus Paren(ExpressionNode** expr_out)
|
||||
{
|
||||
ExpressionParseStatus status;
|
||||
|
||||
// lparen already chewed
|
||||
if ((status = Toplevel(expr_out)) != EXPRESSION_PARSE_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (!Expects(TOK_RPAREN))
|
||||
{
|
||||
delete *expr_out;
|
||||
return EXPRESSION_PARSE_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
return EXPRESSION_PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
ExpressionParseStatus Toplevel(ExpressionNode** expr_out) { return Binary(expr_out); }
|
||||
};
|
||||
|
||||
ControlState Expression::GetValue()
|
||||
{
|
||||
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 ExpressionParseStatus ParseExpressionInner(const std::string& str, ControlFinder& finder,
|
||||
Expression** expr_out)
|
||||
{
|
||||
ExpressionParseStatus status;
|
||||
Expression* expr;
|
||||
*expr_out = nullptr;
|
||||
|
||||
if (str == "")
|
||||
return EXPRESSION_PARSE_SUCCESS;
|
||||
|
||||
Lexer l(str);
|
||||
std::vector<Token> tokens;
|
||||
status = l.Tokenize(tokens);
|
||||
if (status != EXPRESSION_PARSE_SUCCESS)
|
||||
return status;
|
||||
|
||||
Parser p(tokens, finder);
|
||||
status = p.Parse(&expr);
|
||||
if (status != EXPRESSION_PARSE_SUCCESS)
|
||||
return status;
|
||||
|
||||
*expr_out = expr;
|
||||
return EXPRESSION_PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
ExpressionParseStatus ParseExpression(const std::string& str, ControlFinder& finder,
|
||||
Expression** expr_out)
|
||||
{
|
||||
// Add compatibility with old simple expressions, which are simple
|
||||
// barewords control names.
|
||||
|
||||
ControlQualifier qualifier;
|
||||
qualifier.control_name = str;
|
||||
qualifier.has_device = false;
|
||||
|
||||
std::shared_ptr<Device> device = finder.FindDevice(qualifier);
|
||||
Device::Control* control = finder.FindControl(qualifier);
|
||||
if (control)
|
||||
{
|
||||
*expr_out = new Expression(new ControlExpression(qualifier, device, control));
|
||||
return EXPRESSION_PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
return ParseExpressionInner(str, finder, expr_out);
|
||||
}
|
||||
}
|
||||
}
|
72
Source/Core/InputCommon/ControlReference/ExpressionParser.h
Normal file
72
Source/Core/InputCommon/ControlReference/ExpressionParser.h
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "InputCommon/ControllerInterface/Device.h"
|
||||
|
||||
namespace ciface
|
||||
{
|
||||
namespace ExpressionParser
|
||||
{
|
||||
class ControlQualifier
|
||||
{
|
||||
public:
|
||||
bool has_device;
|
||||
Core::DeviceQualifier device_qualifier;
|
||||
std::string control_name;
|
||||
|
||||
ControlQualifier() : has_device(false) {}
|
||||
operator std::string()
|
||||
{
|
||||
if (has_device)
|
||||
return device_qualifier.ToString() + ":" + control_name;
|
||||
else
|
||||
return control_name;
|
||||
}
|
||||
};
|
||||
|
||||
class ControlFinder
|
||||
{
|
||||
public:
|
||||
ControlFinder(const Core::DeviceContainer& container_, const Core::DeviceQualifier& default_,
|
||||
const bool is_input_)
|
||||
: container(container_), default_device(default_), is_input(is_input_)
|
||||
{
|
||||
}
|
||||
std::shared_ptr<Core::Device> FindDevice(ControlQualifier qualifier);
|
||||
Core::Device::Control* FindControl(ControlQualifier qualifier);
|
||||
|
||||
private:
|
||||
const Core::DeviceContainer& container;
|
||||
const Core::DeviceQualifier& default_device;
|
||||
bool is_input;
|
||||
};
|
||||
|
||||
class ExpressionNode;
|
||||
class Expression
|
||||
{
|
||||
public:
|
||||
Expression() : node(nullptr) {}
|
||||
Expression(ExpressionNode* node);
|
||||
~Expression();
|
||||
ControlState GetValue();
|
||||
void SetValue(ControlState state);
|
||||
int num_controls;
|
||||
ExpressionNode* node;
|
||||
};
|
||||
|
||||
enum ExpressionParseStatus
|
||||
{
|
||||
EXPRESSION_PARSE_SUCCESS = 0,
|
||||
EXPRESSION_PARSE_SYNTAX_ERROR,
|
||||
EXPRESSION_PARSE_NO_DEVICE,
|
||||
};
|
||||
|
||||
ExpressionParseStatus ParseExpression(const std::string& expr, ControlFinder& finder,
|
||||
Expression** expr_out);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user