Skip to content
Permalink
Browse files

ExpressionParser: Replace the timer literal with a timer function tha…

…t 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.
  • Loading branch information...
jordan-woyak committed Jan 5, 2019
1 parent 785eb14 commit 4dd078568b360198e0d24acf10fede8e23cdb378
Showing with 65 additions and 53 deletions.
  1. +65 −53 Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
@@ -171,12 +171,17 @@ class Lexer
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 @@ class UnarySinExpression : public UnaryExpression
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 @@ class LiteralReal : public LiteralExpression
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 @@ class Parser

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,28 +793,16 @@ class Parser
return {ParseStatus::Successful, std::make_unique<VariableExpression>(tok.data)};
}
case TOK_LPAREN:
{
return Paren();
}
default:
return {ParseStatus::SyntaxError};
}
}

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 @@ class Parser

ParseResult Binary(int precedence = 999)
{
ParseResult lhs = Unary();
ParseResult lhs = Atom();

if (lhs.status == ParseStatus::SyntaxError)
return lhs;

0 comments on commit 4dd0785

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