Skip to content
Permalink
Browse files

ExpressionParser: Add mod operator, sin function, and timer "constant…

…" which can be used for auto-fire and oscillators.
  • Loading branch information...
jordan-woyak committed Dec 30, 2018
1 parent a8f3e95 commit 1efcf861ead76bbffb4ac22726d799975146707c
Showing with 81 additions and 11 deletions.
  1. +81 −11 Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
@@ -4,13 +4,16 @@

#include <algorithm>
#include <cassert>
#include <chrono>
#include <cmath>
#include <iostream>
#include <map>
#include <memory>
#include <regex>
#include <string>
#include <vector>

#include "Common/MathUtil.h"
#include "Common/StringUtil.h"
#include "InputCommon/ControlReference/ExpressionParser.h"

@@ -31,6 +34,7 @@ enum TokenType
TOK_ADD,
TOK_MUL,
TOK_DIV,
TOK_MOD,
TOK_CONTROL,
TOK_LITERAL,
};
@@ -51,6 +55,8 @@ inline std::string OpName(TokenType op)
return "Mul";
case TOK_DIV:
return "Div";
case TOK_MOD:
return "Mod";
default:
assert(false);
return "";
@@ -89,6 +95,8 @@ class Token
return "*";
case TOK_DIV:
return "/";
case TOK_MOD:
return "%";
case TOK_CONTROL:
return "Device(" + data + ")";
case TOK_LITERAL:
@@ -200,6 +208,8 @@ class Lexer
return Token(TOK_MUL);
case '/':
return Token(TOK_DIV);
case '%':
return Token(TOK_MOD);
case '\'':
return GetLiteral();
case '`':
@@ -304,6 +314,11 @@ class BinaryExpression : public Expression
const ControlState result = lhsValue / rhsValue;
return std::isinf(result) ? 0.0 : result;
}
case TOK_MOD:
{
const ControlState result = std::fmod(lhsValue, rhsValue);
return std::isnan(result) ? 0.0 : result;
}
default:
assert(false);
return 0;
@@ -411,6 +426,16 @@ class UnaryNotExpression : public UnaryExpression
std::string GetFuncName() const override { return ""; }
};

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

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

std::unique_ptr<UnaryExpression> MakeUnaryExpression(std::string name,
std::unique_ptr<Expression>&& inner_)
{
@@ -421,21 +446,15 @@ std::unique_ptr<UnaryExpression> MakeUnaryExpression(std::string name,
return std::make_unique<UnaryNotExpression>(std::move(inner_));
else if ("toggle" == name)
return std::make_unique<UnaryToggleExpression>(std::move(inner_));
else if ("sin" == name)
return std::make_unique<UnarySinExpression>(std::move(inner_));
else
return std::make_unique<UnaryUnknownExpression>(std::move(inner_));
}

class LiteralExpression : public Expression
{
public:
explicit LiteralExpression(const std::string& str)
{
// If it fails to parse it will just be the default: 0.0
TryParse(str, &m_value);
}

ControlState GetValue() const override { return m_value; }

void SetValue(ControlState value) override
{
// Do nothing.
@@ -448,12 +467,62 @@ class LiteralExpression : public Expression
// Nothing needed.
}

operator std::string() const override { return '\'' + ValueToString(m_value) + '\''; }
operator std::string() const override { return '\'' + GetName() + '\''; }

protected:
virtual std::string GetName() const = 0;
};

class LiteralReal : public LiteralExpression
{
public:
LiteralReal(ControlState value) : m_value(value) {}

ControlState GetValue() const override { return m_value; }

std::string GetName() const override { return ValueToString(m_value); }

private:
ControlState m_value{};
const ControlState m_value{};
};

// A +1.0 per second incrementing timer:
class LiteralTimer : public LiteralExpression
{
public:
ControlState GetValue() const override
{
const auto ms =
std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now().time_since_epoch());
// TODO: Will this roll over nicely?
return ms.count() / 1000.0;
}

std::string GetName() const override { return "Timer"; }

private:
using Clock = std::chrono::steady_clock;
};

std::unique_ptr<LiteralExpression> MakeLiteralExpression(std::string name)
{
// Case insensitive matching.
std::transform(name.begin(), name.end(), name.begin(), ::tolower);

// Check for named literals:
if ("timer" == name)
{
return std::make_unique<LiteralTimer>();
}
else
{
// Assume it's a Real. If TryParse fails we'll just get a Zero.
ControlState val{};
TryParse(name, &val);
return std::make_unique<LiteralReal>(val);
}
}

// This class proxies all methods to its either left-hand child if it has bound controls, or its
// right-hand child. Its intended use is for supporting old-style barewords expressions.
class CoalesceExpression : public Expression
@@ -552,7 +621,7 @@ class Parser
}
case TOK_LITERAL:
{
return {ParseStatus::Successful, std::make_unique<LiteralExpression>(tok.data)};
return {ParseStatus::Successful, MakeLiteralExpression(tok.data)};
}
case TOK_LPAREN:
return Paren();
@@ -595,6 +664,7 @@ class Parser
case TOK_ADD:
case TOK_MUL:
case TOK_DIV:
case TOK_MOD:
return true;
default:
return false;

0 comments on commit 1efcf86

Please sign in to comment.
You can’t perform that action at this time.