Skip to content

Commit

Permalink
ExpressionParser: Suppport N-ary functions. Arguments are read LISP s…
Browse files Browse the repository at this point in the history
…tyle. N atoms are read after the function name. Added "if" function and made the "while" function more sensible with an arity of 2. Removed the ugly binary conditional operator.
  • Loading branch information
jordan-woyak committed Oct 11, 2019
1 parent 4dd0785 commit 7cf903a
Showing 1 changed file with 99 additions and 79 deletions.
178 changes: 99 additions & 79 deletions Source/Core/InputCommon/ControlReference/ExpressionParser.cpp
Expand Up @@ -22,14 +22,17 @@ namespace ciface::ExpressionParser
{
using namespace ciface::Core;

constexpr int LOOP_MAX_REPS = 10000;
constexpr ControlState CONDITION_THRESHOLD = 0.5;

enum TokenType
{
TOK_DISCARD,
TOK_INVALID,
TOK_EOF,
TOK_LPAREN,
TOK_RPAREN,
TOK_UNARY,
TOK_FUNCTION,
TOK_CONTROL,
TOK_LITERAL,
TOK_VARIABLE,
Expand All @@ -45,7 +48,6 @@ enum TokenType
TOK_ASSIGN,
TOK_LTHAN,
TOK_GTHAN,
TOK_COND,
TOK_COMMA,
TOK_BINARY_OPS_END,
};
Expand All @@ -58,8 +60,8 @@ inline std::string OpName(TokenType op)
return "And";
case TOK_OR:
return "Or";
case TOK_UNARY:
return "Unary";
case TOK_FUNCTION:
return "Function";
case TOK_ADD:
return "Add";
case TOK_SUB:
Expand All @@ -76,8 +78,6 @@ inline std::string OpName(TokenType op)
return "LThan";
case TOK_GTHAN:
return "GThan";
case TOK_COND:
return "Cond";
case TOK_COMMA:
return "Comma";
case TOK_VARIABLE:
Expand Down Expand Up @@ -112,7 +112,7 @@ class Token
return "&";
case TOK_OR:
return "|";
case TOK_UNARY:
case TOK_FUNCTION:
return '!' + data;
case TOK_ADD:
return "+";
Expand All @@ -130,8 +130,6 @@ class Token
return "<";
case TOK_GTHAN:
return ">";
case TOK_COND:
return "?";
case TOK_COMMA:
return ",";
case TOK_CONTROL:
Expand Down Expand Up @@ -188,7 +186,7 @@ class Lexer
return FetchCharsWhile([&rx](char c) { return std::regex_match(std::string(1, c), rx); });
}

Token GetUnaryFunction() { return Token(TOK_UNARY, FetchWordChars()); }
Token GetFunction() { return Token(TOK_FUNCTION, FetchWordChars()); }

Token GetDelimitedLiteral() { return Token(TOK_LITERAL, FetchDelimString('\'')); }

Expand Down Expand Up @@ -239,7 +237,7 @@ class Lexer
case '|':
return Token(TOK_OR);
case '!':
return GetUnaryFunction();
return GetFunction();
case '+':
return Token(TOK_ADD);
case '-':
Expand All @@ -256,8 +254,6 @@ class Lexer
return Token(TOK_LTHAN);
case '>':
return Token(TOK_GTHAN);
case '?':
return Token(TOK_COND);
case ',':
return Token(TOK_COMMA);
case '\'':
Expand Down Expand Up @@ -387,14 +383,6 @@ class BinaryExpression : public Expression
return lhs->GetValue() < rhs->GetValue();
case TOK_GTHAN:
return lhs->GetValue() > rhs->GetValue();
case TOK_COND:
{
constexpr ControlState COND_THRESHOLD = 0.5;
if (lhs->GetValue() > COND_THRESHOLD)
return rhs->GetValue();
else
return 0.0;
}
case TOK_COMMA:
{
// Eval and discard lhs:
Expand Down Expand Up @@ -432,54 +420,70 @@ class BinaryExpression : public Expression
}
};

class UnaryExpression : public Expression
class FunctionExpression : public Expression
{
public:
UnaryExpression(std::unique_ptr<Expression>&& inner_) : inner(std::move(inner_)) {}
int CountNumControls() const override
{
int result = 0;

int CountNumControls() const override { return inner->CountNumControls(); }
void UpdateReferences(ControlEnvironment& env) override { inner->UpdateReferences(env); }
for (auto& arg : m_args)
result += arg->CountNumControls();

return result;
}

void UpdateReferences(ControlEnvironment& env) override
{
for (auto& arg : m_args)
arg->UpdateReferences(env);
}

operator std::string() const override
{
return '!' + GetFuncName() + '(' + static_cast<std::string>(*inner) + ')';
std::string result = '!' + GetFuncName();

for (auto& arg : m_args)
result += ' ' + static_cast<std::string>(*arg);

return result;
}

void AppendArg(std::unique_ptr<Expression> arg) { m_args.emplace_back(std::move(arg)); }

Expression& GetArg(u32 number) { return *m_args[number]; }
const Expression& GetArg(u32 number) const { return *m_args[number]; }
virtual int GetArity() const = 0;

protected:
virtual std::string GetFuncName() const = 0;

std::unique_ptr<Expression> inner;
private:
std::vector<std::unique_ptr<Expression>> m_args;
};

// TODO: Return an oscillating value to make it apparent something was spelled wrong?
class UnaryUnknownExpression : public UnaryExpression
class UnknownFunctionExpression : public FunctionExpression
{
public:
UnaryUnknownExpression(std::unique_ptr<Expression>&& inner_) : UnaryExpression(std::move(inner_))
{
}

ControlState GetValue() const override { return 0.0; }
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "Unknown"; }
int GetArity() const override { return 0; }
};

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

ControlState GetValue() const override
{
const ControlState inner_value = inner->GetValue();
const ControlState inner_value = GetArg(0).GetValue();

if (inner_value < THRESHOLD)
if (inner_value < CONDITION_THRESHOLD)
{
m_released = true;
}
else if (m_released && inner_value > THRESHOLD)
else if (m_released && inner_value > CONDITION_THRESHOLD)
{
m_released = false;
m_state ^= true;
Expand All @@ -490,47 +494,42 @@ class UnaryToggleExpression : public UnaryExpression

void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "Toggle"; }
int GetArity() const override { return 1; }

private:
static constexpr ControlState THRESHOLD = 0.5;
// eww:
mutable bool m_released{};
mutable bool m_state{};
};

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

ControlState GetValue() const override { return 1.0 - inner->GetValue(); }
void SetValue(ControlState value) override { inner->SetValue(1.0 - value); }
ControlState GetValue() const override { return 1.0 - GetArg(0).GetValue(); }
void SetValue(ControlState value) override { GetArg(0).SetValue(1.0 - value); }
std::string GetFuncName() const override { return ""; }
int GetArity() const override { return 1; }
};

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

ControlState GetValue() const override { return std::sin(inner->GetValue()); }
ControlState GetValue() const override { return std::sin(GetArg(0).GetValue()); }
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "Sin"; }
int GetArity() const override { return 1; }
};

class UnaryTimerExpression : public UnaryExpression
class TimerExpression : public FunctionExpression
{
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();
const ControlState val = GetArg(0).GetValue();

ControlState progress = std::chrono::duration_cast<FSec>(elapsed).count() / val;

Expand All @@ -552,57 +551,74 @@ class UnaryTimerExpression : public UnaryExpression
}
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "Timer"; }
int GetArity() const override { return 1; }

private:
using Clock = std::chrono::steady_clock;
mutable Clock::time_point m_start_time = Clock::now();
};

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

ControlState GetValue() const override
{
constexpr int MAX_REPS = 10000;
constexpr ControlState COND_THRESHOLD = 0.5;
return (GetArg(0).GetValue() > CONDITION_THRESHOLD) ? GetArg(1).GetValue() :
GetArg(2).GetValue();
}

void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "If"; }
int GetArity() const override { return 3; }
};

class WhileExpression : public FunctionExpression
{
public:
ControlState GetValue() const override
{
// Returns 1.0 on successful loop, 0.0 on reps exceeded. Sensible?

for (int i = 0; i != MAX_REPS; ++i)
for (int i = 0; i != LOOP_MAX_REPS; ++i)
{
const ControlState val = inner->GetValue();
if (val < COND_THRESHOLD)
// Check condition of 1st argument:
const ControlState val = GetArg(0).GetValue();
if (val < CONDITION_THRESHOLD)
return 1.0;

// Evaluate 2nd argument:
GetArg(1).GetValue();
}

// Exceeded max reps:
return 0.0;
}

void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "While"; }
int GetArity() const override { return 2; }
};

std::unique_ptr<UnaryExpression> MakeUnaryExpression(std::string name,
std::unique_ptr<Expression>&& inner_)
std::unique_ptr<FunctionExpression> MakeFunctionExpression(std::string name)
{
// Case insensitive matching.
std::transform(name.begin(), name.end(), name.begin(),
[](char c) { return std::tolower(c, std::locale::classic()); });

if (name.empty())
return std::make_unique<UnaryNotExpression>(std::move(inner_));
return std::make_unique<NotExpression>();
else if ("if" == name)
return std::make_unique<IfExpression>();
else if ("sin" == name)
return std::make_unique<UnarySinExpression>(std::move(inner_));
return std::make_unique<SinExpression>();
else if ("timer" == name)
return std::make_unique<UnaryTimerExpression>(std::move(inner_));
return std::make_unique<TimerExpression>();
else if ("toggle" == name)
return std::make_unique<UnaryToggleExpression>(std::move(inner_));
return std::make_unique<ToggleExpression>();
else if ("while" == name)
return std::make_unique<UnaryWhileExpression>(std::move(inner_));
return std::make_unique<WhileExpression>();
else
return std::make_unique<UnaryUnknownExpression>(std::move(inner_));
return std::make_unique<UnknownFunctionExpression>();
}

class LiteralExpression : public Expression
Expand Down Expand Up @@ -771,12 +787,19 @@ class Parser
const Token tok = Chew();
switch (tok.type)
{
case TOK_UNARY:
case TOK_FUNCTION:
{
ParseResult result = Atom();
if (result.status == ParseStatus::SyntaxError)
return result;
return {ParseStatus::Successful, MakeUnaryExpression(tok.data, std::move(result.expr))};
auto func = MakeFunctionExpression(tok.data);
int arity = func->GetArity();
while (arity--)
{
auto arg = Atom();
if (arg.status == ParseStatus::SyntaxError)
return arg;

func->AppendArg(std::move(arg.expr));
}
return {ParseStatus::Successful, std::move(func)};
}
case TOK_CONTROL:
{
Expand All @@ -801,8 +824,6 @@ class Parser
}
}

static bool IsUnaryExpression(TokenType type) { return TOK_UNARY == type; }

static bool IsBinaryToken(TokenType type)
{
return type >= TOK_BINARY_OPS_BEGIN && type < TOK_BINARY_OPS_END;
Expand All @@ -826,7 +847,6 @@ class Parser
return 4;
case TOK_OR:
return 5;
case TOK_COND:
case TOK_ASSIGN:
return 6;
case TOK_COMMA:
Expand Down

0 comments on commit 7cf903a

Please sign in to comment.