mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-19 20:41:16 +01:00
b64e882ba5
Without clearing out the "accumulator" for the backtick parsing, our control name was full of junk (the previous device name) causing us to not correctly find the control. Ensure that always we clear the "accumulator" string during backtick parsing.
578 lines
10 KiB
C++
578 lines
10 KiB
C++
|
|
#include "ExpressionParser.h"
|
|
|
|
#include <cassert>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
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_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_ADD:
|
|
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)
|
|
{
|
|
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 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 int CountNumControls()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
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);
|
|
case TOK_ADD:
|
|
return std::min(lhsValue + rhsValue, 1.0f);
|
|
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 int CountNumControls()
|
|
{
|
|
return lhs->CountNumControls() + rhs->CountNumControls();
|
|
}
|
|
|
|
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 int CountNumControls()
|
|
{
|
|
return inner->CountNumControls();
|
|
}
|
|
|
|
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 (!device)
|
|
return NULL;
|
|
|
|
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:
|
|
{
|
|
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:
|
|
case TOK_ADD:
|
|
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);
|
|
}
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
ExpressionParseStatus ParseExpressionInner(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<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(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;
|
|
|
|
Device::Control *control = finder.FindControl(qualifier);
|
|
if (control) {
|
|
*expr_out = new Expression(new ControlExpression(qualifier, control));
|
|
return EXPRESSION_PARSE_SUCCESS;
|
|
}
|
|
|
|
return ParseExpressionInner(str, finder, expr_out);
|
|
}
|
|
|
|
}
|
|
}
|