Skip to content

Commit

Permalink
ExpressionParser: Expand ! symbol to allow for named unary functions.…
Browse files Browse the repository at this point in the history
… Added !toggle function which toggles on/off with each activation of its inner expression.
  • Loading branch information
jordan-woyak committed Oct 11, 2019
1 parent bf63f85 commit a8f3e95
Showing 1 changed file with 97 additions and 33 deletions.
130 changes: 97 additions & 33 deletions Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
Expand Up @@ -7,6 +7,7 @@
#include <iostream>
#include <map>
#include <memory>
#include <regex>
#include <string>
#include <vector>

Expand All @@ -26,7 +27,7 @@ enum TokenType
TOK_RPAREN,
TOK_AND,
TOK_OR,
TOK_NOT,
TOK_UNARY,
TOK_ADD,
TOK_MUL,
TOK_DIV,
Expand All @@ -42,8 +43,8 @@ inline std::string OpName(TokenType op)
return "And";
case TOK_OR:
return "Or";
case TOK_NOT:
return "Not";
case TOK_UNARY:
return "Unary";
case TOK_ADD:
return "Add";
case TOK_MUL:
Expand Down Expand Up @@ -80,8 +81,8 @@ class Token
return "&";
case TOK_OR:
return "|";
case TOK_NOT:
return "!";
case TOK_UNARY:
return "!" + data;
case TOK_ADD:
return "+";
case TOK_MUL:
Expand Down Expand Up @@ -122,6 +123,21 @@ class Lexer
return false;
}

Token GetUnaryFunction()
{
std::string name;

std::regex valid_name_char("[a-z0-9_]", std::regex_constants::icase);

while (it != expr.end() && std::regex_match(std::string(1, *it), valid_name_char))
{
name += *it;
++it;
}

return Token(TOK_UNARY, name);
}

Token GetLiteral()
{
std::string value;
Expand Down Expand Up @@ -177,7 +193,7 @@ class Lexer
case '|':
return Token(TOK_OR);
case '!':
return Token(TOK_NOT);
return GetUnaryFunction();
case '+':
return Token(TOK_ADD);
case '*':
Expand Down Expand Up @@ -322,44 +338,93 @@ class BinaryExpression : public Expression
class UnaryExpression : public Expression
{
public:
TokenType op;
UnaryExpression(std::unique_ptr<Expression>&& inner_) : inner(std::move(inner_)) {}

int CountNumControls() const override { return inner->CountNumControls(); }
void UpdateReferences(ControlFinder& finder) override { inner->UpdateReferences(finder); }

operator std::string() const override
{
return "!" + GetFuncName() + "(" + (std::string)(*inner) + ")";
}

protected:
virtual std::string GetFuncName() const = 0;

std::unique_ptr<Expression> inner;
};

UnaryExpression(TokenType op_, std::unique_ptr<Expression>&& inner_)
: op(op_), inner(std::move(inner_))
// TODO: Return an oscillating value to make it apparent something was spelled wrong?
class UnaryUnknownExpression : public UnaryExpression
{
public:
UnaryUnknownExpression(std::unique_ptr<Expression>&& inner_) : UnaryExpression(std::move(inner_))
{
}
ControlState GetValue() const override

ControlState GetValue() const override { return 0.0; }
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "Unknown"; }
};

class UnaryToggleExpression : public UnaryExpression
{
public:
UnaryToggleExpression(std::unique_ptr<Expression>&& inner_) : UnaryExpression(std::move(inner_))
{
ControlState value = inner->GetValue();
switch (op)
{
case TOK_NOT:
return 1.0 - value;
default:
assert(false);
return 0;
}
}

void SetValue(ControlState value) override
ControlState GetValue() const override
{
switch (op)
{
case TOK_NOT:
inner->SetValue(1.0 - value);
break;
const ControlState inner_value = inner->GetValue();

default:
assert(false);
if (inner_value < THRESHOLD)
{
m_released = true;
}
else if (m_released && inner_value > THRESHOLD)
{
m_released = false;
m_state ^= true;
}

return m_state;
}

int CountNumControls() const override { return inner->CountNumControls(); }
void UpdateReferences(ControlFinder& finder) override { inner->UpdateReferences(finder); }
operator std::string() const override { return OpName(op) + "(" + (std::string)(*inner) + ")"; }
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "Toggle"; }

private:
static constexpr ControlState THRESHOLD = 0.5;
// eww:
mutable bool m_released{};
mutable bool m_state{};
};

class UnaryNotExpression : public UnaryExpression
{
public:
UnaryNotExpression(std::unique_ptr<Expression>&& inner_) : UnaryExpression(std::move(inner_)) {}

ControlState GetValue() const override { return 1.0 - inner->GetValue(); }
void SetValue(ControlState value) override { inner->SetValue(1.0 - value); }
std::string GetFuncName() const override { return ""; }
};

std::unique_ptr<UnaryExpression> MakeUnaryExpression(std::string name,
std::unique_ptr<Expression>&& inner_)
{
// Case insensitive matching.
std::transform(name.begin(), name.end(), name.begin(), ::tolower);

if ("" == name)
return std::make_unique<UnaryNotExpression>(std::move(inner_));
else if ("toggle" == name)
return std::make_unique<UnaryToggleExpression>(std::move(inner_));
else
return std::make_unique<UnaryUnknownExpression>(std::move(inner_));
}

class LiteralExpression : public Expression
{
public:
Expand Down Expand Up @@ -500,7 +565,7 @@ class Parser
{
switch (type)
{
case TOK_NOT:
case TOK_UNARY:
return true;
default:
return false;
Expand All @@ -515,8 +580,7 @@ class Parser
ParseResult result = Atom();
if (result.status == ParseStatus::SyntaxError)
return result;
return {ParseStatus::Successful,
std::make_unique<UnaryExpression>(tok.type, std::move(result.expr))};
return {ParseStatus::Successful, MakeUnaryExpression(tok.data, std::move(result.expr))};
}

return Atom();
Expand Down

0 comments on commit a8f3e95

Please sign in to comment.