ExpressionParser: Move FunctionExpression type definitions into another file.

This commit is contained in:
Jordan Woyak 2019-01-26 12:17:30 -06:00
parent d4f9b8c4ef
commit fd07ae8cec
7 changed files with 316 additions and 256 deletions

View File

@ -45,6 +45,8 @@ add_library(inputcommon
ControlReference/ControlReference.h ControlReference/ControlReference.h
ControlReference/ExpressionParser.cpp ControlReference/ExpressionParser.cpp
ControlReference/ExpressionParser.h ControlReference/ExpressionParser.h
ControlReference/FunctionExpression.cpp
ControlReference/FunctionExpression.h
) )
target_link_libraries(inputcommon PUBLIC target_link_libraries(inputcommon PUBLIC

View File

@ -2,29 +2,24 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <algorithm>
#include <cassert> #include <cassert>
#include <chrono>
#include <cmath> #include <cmath>
#include <iostream> #include <iostream>
#include <locale>
#include <map>
#include <memory> #include <memory>
#include <regex> #include <regex>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include "Common/MathUtil.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "InputCommon/ControlReference/ExpressionParser.h" #include "InputCommon/ControlReference/ExpressionParser.h"
#include "InputCommon/ControlReference/FunctionExpression.h"
namespace ciface::ExpressionParser namespace ciface::ExpressionParser
{ {
using namespace ciface::Core; using namespace ciface::Core;
constexpr int LOOP_MAX_REPS = 10000;
constexpr ControlState CONDITION_THRESHOLD = 0.5;
enum TokenType enum TokenType
{ {
TOK_DISCARD, TOK_DISCARD,
@ -420,253 +415,6 @@ public:
} }
}; };
class FunctionExpression : public Expression
{
public:
int CountNumControls() const override
{
int result = 0;
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
{
std::string result = '!' + GetFuncName();
for (auto& arg : m_args)
result += ' ' + static_cast<std::string>(*arg);
return result;
}
bool SetArguments(std::vector<std::unique_ptr<Expression>>&& args)
{
m_args = std::move(args);
return ValidateArguments(m_args);
}
protected:
virtual std::string GetFuncName() const = 0;
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) = 0;
Expression& GetArg(u32 number) { return *m_args[number]; }
const Expression& GetArg(u32 number) const { return *m_args[number]; }
private:
std::vector<std::unique_ptr<Expression>> m_args;
};
// TODO: Return an oscillating value to make it apparent something was spelled wrong?
class UnknownFunctionExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return false;
}
ControlState GetValue() const override { return 0.0; }
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "unknown"; }
};
class ToggleExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 1 == args.size();
}
ControlState GetValue() const override
{
const ControlState inner_value = GetArg(0).GetValue();
if (inner_value < CONDITION_THRESHOLD)
{
m_released = true;
}
else if (m_released && inner_value > CONDITION_THRESHOLD)
{
m_released = false;
m_state ^= true;
}
return m_state;
}
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "toggle"; }
mutable bool m_released{};
mutable bool m_state{};
};
class NotExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 1 == args.size();
}
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 ""; }
};
class SinExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 1 == args.size();
}
ControlState GetValue() const override { return std::sin(GetArg(0).GetValue()); }
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "sin"; }
};
class TimerExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 1 == args.size();
}
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 = GetArg(0).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 IfExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 3 == args.size();
}
ControlState GetValue() const override
{
return (GetArg(0).GetValue() > CONDITION_THRESHOLD) ? GetArg(1).GetValue() :
GetArg(2).GetValue();
}
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "if"; }
};
class UnaryMinusExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 1 == args.size();
}
ControlState GetValue() const override
{
// Subtraction for clarity:
return 0.0 - GetArg(0).GetValue();
}
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "minus"; }
};
class WhileExpression : public FunctionExpression
{
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 2 == args.size();
}
ControlState GetValue() const override
{
// Returns 1.0 on successful loop, 0.0 on reps exceeded. Sensible?
for (int i = 0; i != LOOP_MAX_REPS; ++i)
{
// 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"; }
};
std::unique_ptr<FunctionExpression> MakeFunctionExpression(std::string name)
{
if (name.empty())
return std::make_unique<NotExpression>();
else if ("if" == name)
return std::make_unique<IfExpression>();
else if ("sin" == name)
return std::make_unique<SinExpression>();
else if ("timer" == name)
return std::make_unique<TimerExpression>();
else if ("toggle" == name)
return std::make_unique<ToggleExpression>();
else if ("while" == name)
return std::make_unique<WhileExpression>();
else if ("minus" == name)
return std::make_unique<UnaryMinusExpression>();
else
return std::make_unique<UnknownFunctionExpression>();
}
class LiteralExpression : public Expression class LiteralExpression : public Expression
{ {
public: public:

View File

@ -7,7 +7,7 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility>
#include "InputCommon/ControllerInterface/Device.h" #include "InputCommon/ControllerInterface/Device.h"
namespace ciface::ExpressionParser namespace ciface::ExpressionParser

View File

@ -0,0 +1,261 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <chrono>
#include <cmath>
#include "InputCommon/ControlReference/FunctionExpression.h"
namespace ciface
{
namespace ExpressionParser
{
constexpr int LOOP_MAX_REPS = 10000;
constexpr ControlState CONDITION_THRESHOLD = 0.5;
// TODO: Return an oscillating value to make it apparent something was spelled wrong?
class UnknownFunctionExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return false;
}
ControlState GetValue() const override { return 0.0; }
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "unknown"; }
};
class ToggleExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 1 == args.size();
}
ControlState GetValue() const override
{
const ControlState inner_value = GetArg(0).GetValue();
if (inner_value < CONDITION_THRESHOLD)
{
m_released = true;
}
else if (m_released && inner_value > CONDITION_THRESHOLD)
{
m_released = false;
m_state ^= true;
}
return m_state;
}
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "toggle"; }
mutable bool m_released{};
mutable bool m_state{};
};
class NotExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 1 == args.size();
}
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 ""; }
};
class SinExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 1 == args.size();
}
ControlState GetValue() const override { return std::sin(GetArg(0).GetValue()); }
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "sin"; }
};
class TimerExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 1 == args.size();
}
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 = GetArg(0).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 IfExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 3 == args.size();
}
ControlState GetValue() const override
{
return (GetArg(0).GetValue() > CONDITION_THRESHOLD) ? GetArg(1).GetValue() :
GetArg(2).GetValue();
}
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "if"; }
};
class UnaryMinusExpression : public FunctionExpression
{
private:
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 1 == args.size();
}
ControlState GetValue() const override
{
// Subtraction for clarity:
return 0.0 - GetArg(0).GetValue();
}
void SetValue(ControlState value) override {}
std::string GetFuncName() const override { return "minus"; }
};
class WhileExpression : public FunctionExpression
{
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
return 2 == args.size();
}
ControlState GetValue() const override
{
// Returns 1.0 on successful loop, 0.0 on reps exceeded. Sensible?
for (int i = 0; i != LOOP_MAX_REPS; ++i)
{
// 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"; }
};
std::unique_ptr<FunctionExpression> MakeFunctionExpression(std::string name)
{
if (name.empty())
return std::make_unique<NotExpression>();
else if ("if" == name)
return std::make_unique<IfExpression>();
else if ("sin" == name)
return std::make_unique<SinExpression>();
else if ("timer" == name)
return std::make_unique<TimerExpression>();
else if ("toggle" == name)
return std::make_unique<ToggleExpression>();
else if ("while" == name)
return std::make_unique<WhileExpression>();
else if ("minus" == name)
return std::make_unique<UnaryMinusExpression>();
else
return std::make_unique<UnknownFunctionExpression>();
}
int FunctionExpression::CountNumControls() const
{
int result = 0;
for (auto& arg : m_args)
result += arg->CountNumControls();
return result;
}
void FunctionExpression::UpdateReferences(ControlEnvironment& env)
{
for (auto& arg : m_args)
arg->UpdateReferences(env);
}
FunctionExpression::operator std::string() const
{
std::string result = '!' + GetFuncName();
for (auto& arg : m_args)
result += ' ' + static_cast<std::string>(*arg);
return result;
}
bool FunctionExpression::SetArguments(std::vector<std::unique_ptr<Expression>>&& args)
{
m_args = std::move(args);
return ValidateArguments(m_args);
}
Expression& FunctionExpression::GetArg(u32 number)
{
return *m_args[number];
}
const Expression& FunctionExpression::GetArg(u32 number) const
{
return *m_args[number];
}
} // namespace ExpressionParser
} // namespace ciface

View File

@ -0,0 +1,41 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "InputCommon/ControlReference/ExpressionParser.h"
#include "InputCommon/ControlReference/FunctionExpression.h"
namespace ciface
{
namespace ExpressionParser
{
class FunctionExpression : public Expression
{
public:
int CountNumControls() const override;
void UpdateReferences(ControlEnvironment& env) override;
operator std::string() const override;
bool SetArguments(std::vector<std::unique_ptr<Expression>>&& args);
protected:
virtual std::string GetFuncName() const = 0;
virtual bool ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) = 0;
Expression& GetArg(u32 number);
const Expression& GetArg(u32 number) const;
private:
std::vector<std::unique_ptr<Expression>> m_args;
};
std::unique_ptr<FunctionExpression> MakeFunctionExpression(std::string name);
} // namespace ExpressionParser
} // namespace ciface

View File

@ -64,6 +64,7 @@
<ClCompile Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.cpp" /> <ClCompile Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.cpp" />
<ClCompile Include="ControllerInterface\Win32\Win32.cpp" /> <ClCompile Include="ControllerInterface\Win32\Win32.cpp" />
<ClCompile Include="ControllerInterface\XInput\XInput.cpp" /> <ClCompile Include="ControllerInterface\XInput\XInput.cpp" />
<ClCompile Include="ControlReference\FunctionExpression.cpp" />
<ClCompile Include="GCAdapter.cpp"> <ClCompile Include="GCAdapter.cpp">
<!-- <!--
Disable "nonstandard extension used : zero-sized array in struct/union" warning, Disable "nonstandard extension used : zero-sized array in struct/union" warning,
@ -100,6 +101,7 @@
<ClInclude Include="ControllerInterface\DInput\DInputKeyboardMouse.h" /> <ClInclude Include="ControllerInterface\DInput\DInputKeyboardMouse.h" />
<ClInclude Include="ControllerInterface\DInput\XInputFilter.h" /> <ClInclude Include="ControllerInterface\DInput\XInputFilter.h" />
<ClInclude Include="ControlReference\ControlReference.h" /> <ClInclude Include="ControlReference\ControlReference.h" />
<ClInclude Include="ControlReference\FunctionExpression.h" />
<ClInclude Include="ControlReference\ExpressionParser.h" /> <ClInclude Include="ControlReference\ExpressionParser.h" />
<ClInclude Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.h" /> <ClInclude Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.h" />
<ClInclude Include="ControllerInterface\Win32\Win32.h" /> <ClInclude Include="ControllerInterface\Win32\Win32.h" />

View File

@ -113,6 +113,9 @@
<ClCompile Include="ControlReference\ControlReference.cpp"> <ClCompile Include="ControlReference\ControlReference.cpp">
<Filter>ControllerInterface</Filter> <Filter>ControllerInterface</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="ControlReference\FunctionExpression.cpp">
<Filter>ControllerInterface</Filter>
</ClCompile>
<ClCompile Include="InputProfile.cpp" /> <ClCompile Include="InputProfile.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\Attachments.cpp"> <ClCompile Include="ControllerEmu\ControlGroup\Attachments.cpp">
<Filter>ControllerEmu\ControlGroup</Filter> <Filter>ControllerEmu\ControlGroup</Filter>
@ -206,6 +209,9 @@
<ClInclude Include="ControlReference\ControlReference.h"> <ClInclude Include="ControlReference\ControlReference.h">
<Filter>ControllerInterface</Filter> <Filter>ControllerInterface</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="ControlReference\FunctionExpression.h">
<Filter>ControllerInterface</Filter>
</ClCompile>
<ClInclude Include="InputProfile.h" /> <ClInclude Include="InputProfile.h" />
<ClInclude Include="ControllerEmu\ControlGroup\Attachments.h"> <ClInclude Include="ControllerEmu\ControlGroup\Attachments.h">
<Filter>ControllerEmu\ControlGroup</Filter> <Filter>ControllerEmu\ControlGroup</Filter>