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.

This commit is contained in:
Jordan Woyak 2019-01-05 15:31:05 -06:00
parent 785eb14432
commit 4dd078568b

View File

@ -171,12 +171,17 @@ public:
std::string FetchDelimString(char delim) std::string FetchDelimString(char delim)
{ {
const std::string result = FetchCharsWhile([delim](char c) { return c != delim; }); const std::string result = FetchCharsWhile([delim](char c) { return c != delim; });
if (it != expr.end())
++it; ++it;
return result; return result;
} }
std::string FetchWordChars() 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: // Valid word characters:
std::regex rx("[a-z0-9_]", std::regex_constants::icase); std::regex rx("[a-z0-9_]", std::regex_constants::icase);
@ -513,6 +518,46 @@ public:
std::string GetFuncName() const override { return "Sin"; } 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 class UnaryWhileExpression : public UnaryExpression
{ {
public: public:
@ -548,10 +593,12 @@ std::unique_ptr<UnaryExpression> MakeUnaryExpression(std::string name,
if (name.empty()) if (name.empty())
return std::make_unique<UnaryNotExpression>(std::move(inner_)); return std::make_unique<UnaryNotExpression>(std::move(inner_));
else if ("toggle" == name)
return std::make_unique<UnaryToggleExpression>(std::move(inner_));
else if ("sin" == name) else if ("sin" == name)
return std::make_unique<UnarySinExpression>(std::move(inner_)); 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) else if ("while" == name)
return std::make_unique<UnaryWhileExpression>(std::move(inner_)); return std::make_unique<UnaryWhileExpression>(std::move(inner_));
else else
@ -592,43 +639,13 @@ private:
const 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) std::unique_ptr<LiteralExpression> MakeLiteralExpression(std::string name)
{ {
// Case insensitive matching. // If TryParse fails we'll just get a Zero.
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{}; ControlState val{};
TryParse(name, &val); TryParse(name, &val);
return std::make_unique<LiteralReal>(val); return std::make_unique<LiteralReal>(val);
} }
}
class VariableExpression : public Expression class VariableExpression : public Expression
{ {
@ -751,9 +768,16 @@ private:
ParseResult Atom() ParseResult Atom()
{ {
Token tok = Chew(); const Token tok = Chew();
switch (tok.type) 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: case TOK_CONTROL:
{ {
ControlQualifier cq; ControlQualifier cq;
@ -769,7 +793,9 @@ private:
return {ParseStatus::Successful, std::make_unique<VariableExpression>(tok.data)}; return {ParseStatus::Successful, std::make_unique<VariableExpression>(tok.data)};
} }
case TOK_LPAREN: case TOK_LPAREN:
{
return Paren(); return Paren();
}
default: default:
return {ParseStatus::SyntaxError}; return {ParseStatus::SyntaxError};
} }
@ -777,20 +803,6 @@ private:
static bool IsUnaryExpression(TokenType type) { return TOK_UNARY == type; } 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) static bool IsBinaryToken(TokenType type)
{ {
return type >= TOK_BINARY_OPS_BEGIN && type < TOK_BINARY_OPS_END; return type >= TOK_BINARY_OPS_BEGIN && type < TOK_BINARY_OPS_END;
@ -827,7 +839,7 @@ private:
ParseResult Binary(int precedence = 999) ParseResult Binary(int precedence = 999)
{ {
ParseResult lhs = Unary(); ParseResult lhs = Atom();
if (lhs.status == ParseStatus::SyntaxError) if (lhs.status == ParseStatus::SyntaxError)
return lhs; return lhs;