dolphin/Source/Core/InputCommon/ControllerInterface/ExpressionParser.cpp
Léo Lam 77fab5940c InputConfigDiag: Don't prevent closing dialog if device not found
There is no reason to prevent the user from closing the config dialog
if the device is not found. It's not very good UX…

Also fixes ExpressionParser to return NO_DEVICE if the device doesn't
exist instead of SUCCESS.
2016-07-30 10:00:55 +02:00

567 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/ControllerInterface/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()->GetGatedState(); }
void SetValue(ControlState value) override { control->ToOutput()->SetGatedState(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);
}
}
}