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&& 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; + + const ControlState val = inner->GetValue(); + + ControlState progress = std::chrono::duration_cast(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(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 MakeUnaryExpression(std::string name, if (name.empty()) return std::make_unique(std::move(inner_)); - else if ("toggle" == name) - return std::make_unique(std::move(inner_)); else if ("sin" == name) return std::make_unique(std::move(inner_)); + else if ("timer" == name) + return std::make_unique(std::move(inner_)); + else if ("toggle" == name) + return std::make_unique(std::move(inner_)); else if ("while" == name) return std::make_unique(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(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 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(); - } - else - { - // Assume it's a Real. If TryParse fails we'll just get a Zero. - ControlState val{}; - TryParse(name, &val); - return std::make_unique(val); - } + // If TryParse fails we'll just get a Zero. + ControlState val{}; + TryParse(name, &val); + return std::make_unique(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(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;