forked from dolphin-emu/dolphin
		
	Better separation of concerns. Relegates `ControllerInterface` to enumerating input controls, and the new `ControlReference` deals with combining inputs and configuration expression parsing.
		
			
				
	
	
		
			568 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			568 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// 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);
 | 
						|
}
 | 
						|
}
 | 
						|
}
 |