summaryrefslogtreecommitdiff
path: root/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2019-01-05 15:31:05 -0600
committerJordan Woyak <jordan.woyak@gmail.com>2019-10-11 17:14:45 -0500
commit4dd078568b360198e0d24acf10fede8e23cdb378 (patch)
treef3e772951d55a1c14d72d0cc2d1203d0c892ee99 /Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
parent785eb144322f8d77c3697ccc30cf98fc231ed239 (diff)
ExpressionParser: Replace the timer literal with a timer function that increases from 0.0 to 1.0 and resets after N seconds. e.g. (!timer 2.0) is a 2 second timer. Fixed parsing of unary expressions so things like (! ! 1.0) work.
Diffstat (limited to 'Source/Core/InputCommon/ControlReference/ExpressionParser.cpp')
-rw-r--r--Source/Core/InputCommon/ControlReference/ExpressionParser.cpp118
1 files changed, 65 insertions, 53 deletions
diff --git a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
index af3b9c6fd3..f8024c328f 100644
--- a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
+++ b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
@@ -171,12 +171,17 @@ public:
std::string FetchDelimString(char delim)
{
const std::string result = FetchCharsWhile([delim](char c) { return c != delim; });
- ++it;
+ if (it != expr.end())
+ ++it;
return result;
}
std::string FetchWordChars()
{
+ // Words must start with a letter or underscore.
+ if (expr.end() == it || (!std::isalpha(*it, std::locale::classic()) && ('_' != *it)))
+ return "";
+
// Valid word characters:
std::regex rx("[a-z0-9_]", std::regex_constants::icase);
@@ -513,6 +518,46 @@ public:
std::string GetFuncName() const override { return "Sin"; }
};
+class UnaryTimerExpression : public UnaryExpression
+{
+public:
+ UnaryTimerExpression(std::unique_ptr<Expression>&& inner_) : UnaryExpression(std::move(inner_)) {}
+
+ ControlState GetValue() const override
+ {
+ const auto now = Clock::now();
+ const auto elapsed = now - m_start_time;
+
+ using FSec = std::chrono::duration<ControlState>;
+
+ const ControlState val = inner->GetValue();
+
+ ControlState progress = std::chrono::duration_cast<FSec>(elapsed).count() / val;
+
+ if (std::isinf(progress))
+ {
+ // User configured a 0.0 length timer. Reset the timer and return 0.0.
+ progress = 0.0;
+ m_start_time = now;
+ }
+ else if (progress >= 1.0)
+ {
+ const ControlState reset_count = std::floor(progress);
+
+ m_start_time += std::chrono::duration_cast<Clock::duration>(FSec(val * reset_count));
+ progress -= reset_count;
+ }
+
+ return progress;
+ }
+ void SetValue(ControlState value) override {}
+ std::string GetFuncName() const override { return "Timer"; }
+
+private:
+ using Clock = std::chrono::steady_clock;
+ mutable Clock::time_point m_start_time = Clock::now();
+};
+
class UnaryWhileExpression : public UnaryExpression
{
public:
@@ -548,10 +593,12 @@ std::unique_ptr<UnaryExpression> MakeUnaryExpression(std::string name,
if (name.empty())
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 if ("timer" == name)
+ return std::make_unique<UnaryTimerExpression>(std::move(inner_));
+ else if ("toggle" == name)
+ return std::make_unique<UnaryToggleExpression>(std::move(inner_));
else if ("while" == name)
return std::make_unique<UnaryWhileExpression>(std::move(inner_));
else
@@ -592,42 +639,12 @@ private:
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(),
- [](char c) { return std::tolower(c, std::locale::classic()); });
-
- // 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);
- }
+ // If TryParse fails we'll just get a Zero.
+ ControlState val{};
+ TryParse(name, &val);
+ return std::make_unique<LiteralReal>(val);
}
class VariableExpression : public Expression
@@ -751,9 +768,16 @@ private:
ParseResult Atom()
{
- Token tok = Chew();
+ const Token tok = Chew();
switch (tok.type)
{
+ case TOK_UNARY:
+ {
+ ParseResult result = Atom();
+ if (result.status == ParseStatus::SyntaxError)
+ return result;
+ return {ParseStatus::Successful, MakeUnaryExpression(tok.data, std::move(result.expr))};
+ }
case TOK_CONTROL:
{
ControlQualifier cq;
@@ -769,7 +793,9 @@ private:
return {ParseStatus::Successful, std::make_unique<VariableExpression>(tok.data)};
}
case TOK_LPAREN:
+ {
return Paren();
+ }
default:
return {ParseStatus::SyntaxError};
}
@@ -777,20 +803,6 @@ private:
static bool IsUnaryExpression(TokenType type) { return TOK_UNARY == type; }
- ParseResult Unary()
- {
- if (IsUnaryExpression(Peek().type))
- {
- const Token tok = Chew();
- ParseResult result = Atom();
- if (result.status == ParseStatus::SyntaxError)
- return result;
- return {ParseStatus::Successful, MakeUnaryExpression(tok.data, std::move(result.expr))};
- }
-
- return Atom();
- }
-
static bool IsBinaryToken(TokenType type)
{
return type >= TOK_BINARY_OPS_BEGIN && type < TOK_BINARY_OPS_END;
@@ -827,7 +839,7 @@ private:
ParseResult Binary(int precedence = 999)
{
- ParseResult lhs = Unary();
+ ParseResult lhs = Atom();
if (lhs.status == ParseStatus::SyntaxError)
return lhs;