diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 265f5a433d..518bdbb3ba 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -3,7 +3,8 @@ set(SRCS Src/ControllerEmu.cpp Src/UDPWiimote.cpp Src/UDPWrapper.cpp Src/ControllerInterface/ControllerInterface.cpp - Src/ControllerInterface/Device.cpp) + Src/ControllerInterface/Device.cpp + Src/ControllerInterface/ExpressionParser.cpp) if(WIN32) set(SRCS ${SRCS} diff --git a/Source/Core/InputCommon/InputCommon.vcxproj b/Source/Core/InputCommon/InputCommon.vcxproj index c3243854a2..03aaf1b3ea 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj +++ b/Source/Core/InputCommon/InputCommon.vcxproj @@ -167,6 +167,7 @@ + @@ -180,6 +181,7 @@ + diff --git a/Source/Core/InputCommon/InputCommon.vcxproj.filters b/Source/Core/InputCommon/InputCommon.vcxproj.filters index b4d5ff074f..ce4558f1ad 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj.filters +++ b/Source/Core/InputCommon/InputCommon.vcxproj.filters @@ -23,6 +23,12 @@ ControllerInterface\DInput + + ControllerInterface + + + ControllerInterface + @@ -42,6 +48,9 @@ ControllerInterface\Device + + ControllerInterface + ControllerInterface\DInput @@ -72,4 +81,4 @@ {5a9c1b94-2eab-4357-b44f-87d5db50da3d} - \ No newline at end of file + diff --git a/Source/Core/InputCommon/Src/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/Src/ControllerInterface/ControllerInterface.cpp index e672e4598c..58e0653bbe 100644 --- a/Source/Core/InputCommon/Src/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/Src/ControllerInterface/ControllerInterface.cpp @@ -21,6 +21,8 @@ #include "Thread.h" +using namespace ciface::ExpressionParser; + namespace { const float INPUT_DETECT_THRESHOLD = 0.55f; @@ -187,48 +189,10 @@ bool ControllerInterface::UpdateOutput(const bool force) // ControlState ControllerInterface::InputReference::State( const ControlState ignore ) { - //if (NULL == device) - //return 0; - - ControlState state = 0; - - std::vector::const_iterator - ci = m_controls.begin(), - ce = m_controls.end(); - - // bit of hax for "NOT" to work at start of expression - if (ci != ce) - { - if (ci->mode == 2) - state = 1; - } - - for (; ci!=ce; ++ci) - { - const ControlState istate = ci->control->ToInput()->GetState(); - - switch (ci->mode) - { - // OR - case 0 : - state = std::max(state, istate); - break; - // AND - case 1 : - state = std::min(state, istate); - break; - // NOT - case 2 : - state = std::max(std::min(state, 1.0f - istate), 0.0f); - break; - // ADD - case 3 : - state += istate; - break; - } - } - - return std::min(1.0f, state * range); + if (parsed_expression) + return parsed_expression->GetValue(); + else + return 0.0f; } // @@ -240,17 +204,9 @@ ControlState ControllerInterface::InputReference::State( const ControlState igno // ControlState ControllerInterface::OutputReference::State(const ControlState state) { - const ControlState tmp_state = std::min(1.0f, state * range); - - // output ref just ignores the modes ( |&!... ) - - std::vector::iterator - ci = m_controls.begin(), - ce = m_controls.end(); - for (; ci != ce; ++ci) - ci->control->ToOutput()->SetState(tmp_state); - - return state; // just return the output, watever + if (parsed_expression) + parsed_expression->SetValue(state); + return 0.0f; } // @@ -262,65 +218,13 @@ ControlState ControllerInterface::OutputReference::State(const ControlState stat void ControllerInterface::UpdateReference(ControllerInterface::ControlReference* ref , const DeviceQualifier& default_device) const { - ref->m_controls.clear(); + delete ref->parsed_expression; + ref->parsed_expression = NULL; - // adding | to parse the last item, silly - std::istringstream ss(ref->expression + '|'); - - const std::string mode_chars("|&!^"); - - ControlReference::DeviceControl devc; - - std::string dev_str; - std::string ctrl_str; - - char c = 0; - while (ss.read(&c, 1)) - { - const size_t f = mode_chars.find(c); - - if (mode_chars.npos != f) - { - // add ctrl - if (ctrl_str.size()) - { - DeviceQualifier devq; - - // using default device or alterate device inside `backticks` - if (dev_str.empty()) - devq = default_device; - else - devq.FromString(dev_str); - - // find device - Device* const def_device = FindDevice(devq); - - if (def_device) - { - if (ref->is_input) - devc.control = FindInput(ctrl_str, def_device); - else - devc.control = FindOutput(ctrl_str, def_device); - - if (devc.control) - ref->m_controls.push_back(devc); - } - } - // reset stuff for next ctrl - devc.mode = (int)f; - ctrl_str.clear(); - } - else if ('`' == c) - { - // different device - if (std::getline(ss, dev_str, '`').eof()) - break; // no terminating '`' character - } - else - { - ctrl_str += c; - } - } + ControlFinder finder(*this, default_device, ref->is_input); + ExpressionParseStatus status; + status = ParseExpression(ref->expression, finder, &ref->parsed_expression); + // XXX: do something with status? } // @@ -388,7 +292,7 @@ Device::Control* ControllerInterface::OutputReference::Detect(const unsigned int // ignore device // don't hang if we don't even have any controls mapped - if (m_controls.size()) + if (BoundCount() > 0) { State(1); unsigned int slept = 0; diff --git a/Source/Core/InputCommon/Src/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/Src/ControllerInterface/ControllerInterface.h index db3a82bf3b..57903a08a7 100644 --- a/Source/Core/InputCommon/Src/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/Src/ControllerInterface/ControllerInterface.h @@ -9,6 +9,7 @@ #include "Common.h" #include "Thread.h" +#include "ExpressionParser.h" #include "Device.h" // enable disable sources @@ -53,30 +54,28 @@ public: class ControlReference { friend class ControllerInterface; - public: - virtual ~ControlReference() {} - virtual ControlState State(const ControlState state = 0) = 0; virtual Device::Control* Detect(const unsigned int ms, Device* const device) = 0; - size_t BoundCount() const { return m_controls.size(); } - ControlState range; + ControlState range; std::string expression; const bool is_input; + 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) {} - - struct DeviceControl - { - DeviceControl() : control(NULL), mode(0) {} - - Device::Control* control; - int mode; - }; - - std::vector m_controls; + ControlReference(const bool _is_input) : range(1), is_input(_is_input), parsed_expression(NULL) {} + ciface::ExpressionParser::Expression *parsed_expression; }; // diff --git a/Source/Core/InputCommon/Src/ControllerInterface/ExpressionParser.cpp b/Source/Core/InputCommon/Src/ControllerInterface/ExpressionParser.cpp new file mode 100644 index 0000000000..81ceab5913 --- /dev/null +++ b/Source/Core/InputCommon/Src/ControllerInterface/ExpressionParser.cpp @@ -0,0 +1,516 @@ + +#include "ExpressionParser.h" + +#include +#include +#include +#include +#include + +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_CONTROL, +}; + +inline std::string OpName(TokenType op) +{ + switch (op) + { + case TOK_AND: + return "And"; + case TOK_OR: + return "Or"; + case TOK_NOT: + return "Not"; + 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_INVALID: + return "Invalid"; + 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_CONTROL: + return "Device(" + (std::string)qualifier + ")"; + } + } +}; + +class Lexer { +public: + std::string expr; + std::string::iterator it; + + Lexer(std::string expr_) : expr(expr_) + { + it = expr.begin(); + } + + bool FetchBacktickString(std::string &value, char otherDelim = 0) + { + while (it != expr.end()) + { + char c = *it; + if (c == '`') + return false; + if (c > 0 && c == otherDelim) + return true; + value += c; + it++; + } + return false; + } + + Token GetControlQualifier() + { + 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 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 GetControlQualifier(); + default: + return Token(TOK_INVALID); + } + } + + ExpressionParseStatus Tokenize(std::vector &tokens) + { + while (true) + { + Token tok = NextToken(); + + if (tok.type == TOK_DISCARD) + continue; + + if (tok.type == TOK_INVALID) + { + tokens.empty(); + 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 operator std::string() { return ""; } +}; + +class ControlExpression : public ExpressionNode +{ +public: + ControlQualifier qualifier; + Device::Control *control; + + ControlExpression(ControlQualifier qualifier_, Device::Control *control_) : qualifier(qualifier_), control(control_) {} + + virtual ControlState GetValue() + { + return control->ToInput()->GetState(); + } + + virtual void SetValue(ControlState value) + { + control->ToOutput()->SetState(value); + } + + virtual operator std::string() + { + return "`" + (std::string)qualifier + "`"; + } +}; + +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; + } + + virtual ControlState GetValue() + { + 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); + default: + assert(false); + return 0; + } + } + + virtual void SetValue(ControlState value) + { + // Don't do anything special with the op we have. + // Treat "A & B" the same as "A | B". + lhs->SetValue(value); + rhs->SetValue(value); + } + + virtual operator std::string() + { + 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; + } + + virtual ControlState GetValue() + { + ControlState value = inner->GetValue(); + switch (op) + { + case TOK_NOT: + return 1.0f - value; + default: + assert(false); + return 0; + } + } + + virtual void SetValue(ControlState value) + { + switch (op) + { + case TOK_NOT: + inner->SetValue(1.0f - value); + default: + assert(false); + } + } + + virtual operator std::string() + { + return OpName(op) + "(" + (std::string)(*inner) + ")"; + } +}; + +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) +{ + Device *device = FindDevice(qualifier); + if (is_input) + return device->FindInput(qualifier.control_name); + else + return device->FindOutput(qualifier.control_name); +} + +class Parser +{ +public: + + Parser(std::vector tokens_, ControlFinder &finder_) : tokens(tokens_), finder(finder_) + { + m_it = tokens.begin(); + } + + ExpressionParseStatus Parse(Expression **expr_out) + { + Expression *expr; + ExpressionNode *expr_node; + ExpressionParseStatus status = Toplevel(&expr_node); + if (status != EXPRESSION_PARSE_SUCCESS) + return status; + + expr = new Expression(); + expr->expr = expr_node; + expr->num_controls = CountNumControls(); + *expr_out = expr; + + return EXPRESSION_PARSE_SUCCESS; + } + +private: + std::vector tokens; + std::vector::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: + { + Device::Control *control = finder.FindControl(tok.qualifier); + if (control == NULL) + return EXPRESSION_PARSE_NO_DEVICE; + + *expr_out = new ControlExpression(tok.qualifier, 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) + { + ExpressionParseStatus status; + + if (IsUnaryExpression(Peek().type)) + { + Token tok = Chew(); + ExpressionNode *atom_expr; + if ((status = Atom(&atom_expr)) != EXPRESSION_PARSE_SUCCESS) + 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: + return true; + default: + return false; + } + } + + ExpressionParseStatus Binary(ExpressionNode **expr_out) + { + ExpressionParseStatus status; + + if ((status = Unary(expr_out)) != EXPRESSION_PARSE_SUCCESS) + return status; + + while (IsBinaryToken(Peek().type)) + { + Token tok = Chew(); + ExpressionNode *unary_expr; + if ((status = Unary(&unary_expr)) != EXPRESSION_PARSE_SUCCESS) + { + 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); + } + + int CountNumControls() + { + int count = 0; + for (std::vector::iterator it = tokens.begin(); it != tokens.end(); ++it) + if (it->type == TOK_CONTROL) + count++; + return count; + } +}; + +ControlState Expression::GetValue() +{ + return expr->GetValue(); +} + +void Expression::SetValue(ControlState value) +{ + expr->SetValue(value); +} + +Expression::~Expression() +{ + delete expr; +} + +ExpressionParseStatus ParseExpression(std::string str, ControlFinder &finder, Expression **expr_out) +{ + ExpressionParseStatus status; + Expression *expr; + *expr_out = NULL; + + if (str == "") + return EXPRESSION_PARSE_SUCCESS; + + Lexer l(str); + std::vector 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; +} + +} +} diff --git a/Source/Core/InputCommon/Src/ControllerInterface/ExpressionParser.h b/Source/Core/InputCommon/Src/ControllerInterface/ExpressionParser.h new file mode 100644 index 0000000000..631c84cd9c --- /dev/null +++ b/Source/Core/InputCommon/Src/ControllerInterface/ExpressionParser.h @@ -0,0 +1,73 @@ + +#ifndef _EXPRESSIONPARSER_H_ +#define _EXPRESSIONPARSER_H_ + +#include +#include "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_) {} + Core::Device::Control *FindControl(ControlQualifier qualifier); + +private: + Core::Device *FindDevice(ControlQualifier qualifier); + const Core::DeviceContainer &container; + const Core::DeviceQualifier &default_device; + bool is_input; +}; + +class Parser; +class ExpressionNode; +class Expression +{ + friend class Parser; + +public: + Expression() : expr(NULL) {} + ~Expression(); + ControlState GetValue(); + void SetValue (ControlState state); + int num_controls; + +private: + ExpressionNode *expr; +}; + +enum ExpressionParseStatus +{ + EXPRESSION_PARSE_SUCCESS = 0, + EXPRESSION_PARSE_SYNTAX_ERROR, + EXPRESSION_PARSE_NO_DEVICE, +}; + +ExpressionParseStatus ParseExpression(std::string expr, ControlFinder &finder, Expression **expr_out); + +} +} + +#endif