From fd07ae8cec5c77ac60788350610739e6ac094547 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 26 Jan 2019 12:17:30 -0600 Subject: ExpressionParser: Move FunctionExpression type definitions into another file. --- .../ControlReference/FunctionExpression.cpp | 261 +++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 Source/Core/InputCommon/ControlReference/FunctionExpression.cpp (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp new file mode 100644 index 0000000000..15a66ec9d1 --- /dev/null +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -0,0 +1,261 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#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>& 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>& 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>& 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>& 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>& 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; + + const ControlState val = GetArg(0).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 IfExpression : public FunctionExpression +{ +private: + virtual bool ValidateArguments(const std::vector>& 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>& 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>& 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 MakeFunctionExpression(std::string name) +{ + if (name.empty()) + return std::make_unique(); + else if ("if" == name) + return std::make_unique(); + else if ("sin" == name) + return std::make_unique(); + else if ("timer" == name) + return std::make_unique(); + else if ("toggle" == name) + return std::make_unique(); + else if ("while" == name) + return std::make_unique(); + else if ("minus" == name) + return std::make_unique(); + else + return std::make_unique(); +} + +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(*arg); + + return result; +} + +bool FunctionExpression::SetArguments(std::vector>&& 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 -- cgit v1.2.3 From 6a2096c41977d6c33c2ca20494d9f623bed221ff Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 26 Jan 2019 13:18:20 -0600 Subject: ExpressionParser: Add optional 2nd argument to toggle function which clears state. --- .../InputCommon/ControlReference/FunctionExpression.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index 15a66ec9d1..84f3ac907e 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -32,7 +32,8 @@ class ToggleExpression : public FunctionExpression private: virtual bool ValidateArguments(const std::vector>& args) override { - return 1 == args.size(); + // Optional 2nd argument for clearing state: + return 1 == args.size() || 2 == args.size(); } ControlState GetValue() const override @@ -49,6 +50,11 @@ private: m_state ^= true; } + if (2 == GetArgCount() && GetArg(1).GetValue() > CONDITION_THRESHOLD) + { + m_state = false; + } + return m_state; } @@ -257,5 +263,10 @@ const Expression& FunctionExpression::GetArg(u32 number) const return *m_args[number]; } +u32 FunctionExpression::GetArgCount() const +{ + return u32(m_args.size()); +} + } // namespace ExpressionParser } // namespace ciface -- cgit v1.2.3 From bbd6b1848fcaeaafbe14babb3911557e1a503c76 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 26 Jan 2019 13:35:58 -0600 Subject: ExpressionParser: Add deadzone function. --- .../ControlReference/FunctionExpression.cpp | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index 84f3ac907e..629b8eae0b 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -27,6 +27,7 @@ private: std::string GetFuncName() const override { return "unknown"; } }; +// usage: !toggle(toggle_state_input, [clear_state_input]) class ToggleExpression : public FunctionExpression { private: @@ -65,6 +66,7 @@ private: mutable bool m_state{}; }; +// usage: !not(expression) class NotExpression : public FunctionExpression { private: @@ -78,6 +80,7 @@ private: std::string GetFuncName() const override { return ""; } }; +// usage: !sin(expression) class SinExpression : public FunctionExpression { private: @@ -91,6 +94,7 @@ private: std::string GetFuncName() const override { return "sin"; } }; +// usage: !timer(seconds) class TimerExpression : public FunctionExpression { private: @@ -134,6 +138,7 @@ private: mutable Clock::time_point m_start_time = Clock::now(); }; +// usage: !if(condition, true_expression, false_expression) class IfExpression : public FunctionExpression { private: @@ -152,6 +157,7 @@ private: std::string GetFuncName() const override { return "if"; } }; +// usage: !minus(expression) class UnaryMinusExpression : public FunctionExpression { private: @@ -170,6 +176,7 @@ private: std::string GetFuncName() const override { return "minus"; } }; +// usage: !while(condition, expression) class WhileExpression : public FunctionExpression { virtual bool ValidateArguments(const std::vector>& args) override @@ -200,6 +207,25 @@ class WhileExpression : public FunctionExpression std::string GetFuncName() const override { return "while"; } }; +// usage: deadzone(input, amount) +class DeadzoneExpression : public FunctionExpression +{ + virtual bool ValidateArguments(const std::vector>& args) override + { + return 2 == args.size(); + } + + ControlState GetValue() const override + { + const ControlState val = GetArg(0).GetValue(); + const ControlState deadzone = GetArg(1).GetValue(); + return std::copysign(std::max(0.0, std::abs(val) - deadzone) / (1.0 - deadzone), val); + } + + void SetValue(ControlState value) override {} + std::string GetFuncName() const override { return "deadzone"; } +}; + std::unique_ptr MakeFunctionExpression(std::string name) { if (name.empty()) @@ -216,6 +242,8 @@ std::unique_ptr MakeFunctionExpression(std::string name) return std::make_unique(); else if ("minus" == name) return std::make_unique(); + else if ("deadzone" == name) + return std::make_unique(); else return std::make_unique(); } -- cgit v1.2.3 From 9e536382c44074ae8af620adb605fe107f3ef42c Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 26 Jan 2019 14:04:14 -0600 Subject: ExpressionParser: Add function to smooth inputs. --- .../ControlReference/FunctionExpression.cpp | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index 629b8eae0b..8692d04c78 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -226,6 +226,51 @@ class DeadzoneExpression : public FunctionExpression std::string GetFuncName() const override { return "deadzone"; } }; +// usage: smooth(input, seconds) +// seconds is seconds to change from 0.0 to 1.0 +class SmoothExpression : public FunctionExpression +{ + virtual bool ValidateArguments(const std::vector>& args) override + { + return 2 == args.size(); + } + + ControlState GetValue() const override + { + const auto now = Clock::now(); + const auto elapsed = now - m_last_update; + m_last_update = now; + + const ControlState desired_value = GetArg(0).GetValue(); + const ControlState smooth = GetArg(1).GetValue(); + + using FSec = std::chrono::duration; + + const ControlState max_move = std::chrono::duration_cast(elapsed).count() / smooth; + + if (std::isinf(max_move)) + { + m_value = desired_value; + } + else + { + const ControlState diff = desired_value - m_value; + m_value += std::copysign(std::min(max_move, std::abs(diff)), diff); + } + + return m_value; + } + + void SetValue(ControlState value) override {} + std::string GetFuncName() const override { return "smooth"; } + +private: + using Clock = std::chrono::steady_clock; + + mutable ControlState m_value = 0.0; + mutable Clock::time_point m_last_update = Clock::now(); +}; + std::unique_ptr MakeFunctionExpression(std::string name) { if (name.empty()) @@ -244,6 +289,8 @@ std::unique_ptr MakeFunctionExpression(std::string name) return std::make_unique(); else if ("deadzone" == name) return std::make_unique(); + else if ("smooth" == name) + return std::make_unique(); else return std::make_unique(); } -- cgit v1.2.3 From f2e499d5873eebb8417527deaf694e26a06cb5a9 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 26 Jan 2019 14:15:36 -0600 Subject: ExpressionParser: Add !hold function that activates after input is held for N seconds. --- .../ControlReference/FunctionExpression.cpp | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index 8692d04c78..b7dfe23e02 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -271,6 +271,48 @@ private: mutable Clock::time_point m_last_update = Clock::now(); }; +// usage: !hold(input, seconds) +class HoldExpression : public FunctionExpression +{ + virtual bool ValidateArguments(const std::vector>& args) override + { + return 2 == args.size(); + } + + ControlState GetValue() const override + { + const auto now = Clock::now(); + + const ControlState input = GetArg(0).GetValue(); + + if (input < CONDITION_THRESHOLD) + { + m_state = false; + m_start_time = Clock::now(); + } + else if (!m_state) + { + const auto hold_time = now - m_start_time; + + using FSec = std::chrono::duration; + + if (std::chrono::duration_cast(hold_time).count() >= GetArg(1).GetValue()) + m_state = true; + } + + return m_state; + } + + void SetValue(ControlState value) override {} + std::string GetFuncName() const override { return "smooth"; } + +private: + using Clock = std::chrono::steady_clock; + + mutable bool m_state = false; + mutable Clock::time_point m_start_time = Clock::now(); +}; + std::unique_ptr MakeFunctionExpression(std::string name) { if (name.empty()) @@ -291,6 +333,8 @@ std::unique_ptr MakeFunctionExpression(std::string name) return std::make_unique(); else if ("smooth" == name) return std::make_unique(); + else if ("hold" == name) + return std::make_unique(); else return std::make_unique(); } -- cgit v1.2.3 From 18b51204417bac5f0d436d219885c0ac42e2eb70 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 26 Jan 2019 14:40:47 -0600 Subject: ExpressionParser: Add !tap function which activates after X (defaults to 2) taps within Y seconds. --- .../ControlReference/FunctionExpression.cpp | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index b7dfe23e02..9e6607bce4 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -313,6 +313,68 @@ private: mutable Clock::time_point m_start_time = Clock::now(); }; +// usage: !tap(input, seconds, taps=2) +class TapExpression : public FunctionExpression +{ + virtual bool ValidateArguments(const std::vector>& args) override + { + return 2 == args.size() || 3 == args.size(); + } + + ControlState GetValue() const override + { + const auto now = Clock::now(); + + using FSec = std::chrono::duration; + + const auto elapsed = std::chrono::duration_cast(now - m_start_time).count(); + + const ControlState input = GetArg(0).GetValue(); + const ControlState seconds = GetArg(1).GetValue(); + + const bool is_time_up = elapsed > seconds; + + const u32 desired_taps = (3 == GetArgCount()) ? u32(GetArg(2).GetValue() + 0.5) : 2; + + if (input < CONDITION_THRESHOLD) + { + m_released = true; + + if (m_taps > 0 && is_time_up) + { + m_taps = 0; + } + } + else + { + if (m_released) + { + if (!m_taps) + { + m_start_time = now; + } + + ++m_taps; + m_released = false; + } + + return desired_taps == m_taps; + } + + return 0.0; + } + + void SetValue(ControlState value) override {} + std::string GetFuncName() const override { return "tap"; } + +private: + using Clock = std::chrono::steady_clock; + + mutable bool m_released = true; + mutable u32 m_taps = 0; + mutable Clock::time_point m_start_time = Clock::now(); +}; + std::unique_ptr MakeFunctionExpression(std::string name) { if (name.empty()) @@ -335,6 +397,8 @@ std::unique_ptr MakeFunctionExpression(std::string name) return std::make_unique(); else if ("hold" == name) return std::make_unique(); + else if ("tap" == name) + return std::make_unique(); else return std::make_unique(); } -- cgit v1.2.3 From 08b291b0f9ca3edfd8603a32f2e62f658d012c4a Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 26 Jan 2019 14:48:26 -0600 Subject: ExpressionParser: Fix timer function with negative values. --- Source/Core/InputCommon/ControlReference/FunctionExpression.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index 9e6607bce4..d233519b34 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -114,9 +114,9 @@ private: ControlState progress = std::chrono::duration_cast(elapsed).count() / val; - if (std::isinf(progress)) + if (std::isinf(progress) || progress < 0.0) { - // User configured a 0.0 length timer. Reset the timer and return 0.0. + // User configured a non-positive timer. Reset the timer and return 0.0. progress = 0.0; m_start_time = now; } -- cgit v1.2.3 From b5b43f83426ca3c076d5c8861bde942338fae455 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 26 Jan 2019 15:30:21 -0600 Subject: ExpressionParser: Add relative input function. --- .../ControlReference/FunctionExpression.cpp | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index d233519b34..8596b64221 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -375,6 +375,69 @@ private: mutable Clock::time_point m_start_time = Clock::now(); }; +// usage: !relative(input, speed, [max_abs_value, [shared_state]]) +// speed is max movement per second +class RelativeExpression : public FunctionExpression +{ + virtual bool ValidateArguments(const std::vector>& args) override + { + return args.size() >= 2 && args.size() <= 4; + } + + ControlState GetValue() const override + { + // There is a lot of funky math in this function but it allows for a variety of uses: + // + // e.g. A single mapping with a relatively adjusted value between 0.0 and 1.0 + // Potentially useful for a trigger input + // !relative(`Up` - `Down`, 2.0) + // + // e.g. A value with two mappings (such as analog stick Up/Down) + // The shared state allows the two mappings to work together. + // This mapping (for up) returns a value clamped between 0.0 and 1.0 + // !relative(`Up`, 2.0, 1.0, $y) + // This mapping (for down) returns the negative value clamped between 0.0 and 1.0 + // (Adjustments created by `Down` are applied negatively to the shared state) + // !relative(`Down`, 2.0, -1.0, $y) + + const auto now = Clock::now(); + + if (GetArgCount() >= 4) + m_state = GetArg(3).GetValue(); + + using FSec = std::chrono::duration; + + const auto elapsed = std::chrono::duration_cast(now - m_last_update).count(); + m_last_update = now; + + const ControlState input = GetArg(0).GetValue(); + const ControlState speed = GetArg(1).GetValue(); + + const ControlState max_abs_value = (GetArgCount() >= 3) ? GetArg(2).GetValue() : 1.0; + + const ControlState max_move = input * elapsed * speed; + const ControlState diff_from_zero = std::abs(0.0 - m_state); + const ControlState diff_from_max = std::abs(max_abs_value - m_state); + + m_state += std::min(std::max(max_move, -diff_from_zero), diff_from_max) * + std::copysign(1.0, max_abs_value); + + if (GetArgCount() >= 4) + const_cast(GetArg(3)).SetValue(m_state); + + return std::max(0.0, m_state * std::copysign(1.0, max_abs_value)); + } + + void SetValue(ControlState value) override {} + std::string GetFuncName() const override { return "relative"; } + +private: + using Clock = std::chrono::steady_clock; + + mutable ControlState m_state = 0.0; + mutable Clock::time_point m_last_update = Clock::now(); +}; + std::unique_ptr MakeFunctionExpression(std::string name) { if (name.empty()) @@ -399,6 +462,8 @@ std::unique_ptr MakeFunctionExpression(std::string name) return std::make_unique(); else if ("tap" == name) return std::make_unique(); + else if ("relative" == name) + return std::make_unique(); else return std::make_unique(); } -- cgit v1.2.3 From fae8b15db1042485226ed9ed2d6e9e09a4a54377 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 26 Jan 2019 16:34:28 -0600 Subject: ExpressionParser: Add !pulse function that evaluates to 1.0 for N seconds for each press. --- .../ControlReference/FunctionExpression.cpp | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index 8596b64221..c18ca3e582 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -438,6 +438,61 @@ private: mutable Clock::time_point m_last_update = Clock::now(); }; +// usage: !pulse(input, seconds) +class PulseExpression : public FunctionExpression +{ + virtual bool ValidateArguments(const std::vector>& args) override + { + return 2 == args.size(); + } + + ControlState GetValue() const override + { + const auto now = Clock::now(); + + const ControlState input = GetArg(0).GetValue(); + + if (input < CONDITION_THRESHOLD) + { + m_released = true; + } + else if (m_released) + { + m_released = false; + + using FSec = std::chrono::duration; + const auto seconds = std::chrono::duration_cast(FSec(GetArg(1).GetValue())); + + if (m_state) + { + m_release_time += seconds; + } + else + { + m_state = true; + m_release_time = now + seconds; + } + } + + if (m_state && now >= m_release_time) + { + m_state = false; + } + + return m_state; + } + + void SetValue(ControlState value) override {} + std::string GetFuncName() const override { return "pulse"; } + +private: + using Clock = std::chrono::steady_clock; + + mutable bool m_released = false; + mutable bool m_state = false; + mutable Clock::time_point m_release_time = Clock::now(); +}; + std::unique_ptr MakeFunctionExpression(std::string name) { if (name.empty()) @@ -464,6 +519,8 @@ std::unique_ptr MakeFunctionExpression(std::string name) return std::make_unique(); else if ("relative" == name) return std::make_unique(); + else if ("pulse" == name) + return std::make_unique(); else return std::make_unique(); } -- cgit v1.2.3 From 5cb12486126ac870a6486334fcc156b349c8be2e Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 27 Jan 2019 09:13:12 -0600 Subject: ExpressionParser: Clean up some redundant using-declarations and wrong comments. --- .../ControlReference/FunctionExpression.cpp | 29 ++++------------------ 1 file changed, 5 insertions(+), 24 deletions(-) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index c18ca3e582..724dddedf0 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -14,6 +14,9 @@ namespace ExpressionParser constexpr int LOOP_MAX_REPS = 10000; constexpr ControlState CONDITION_THRESHOLD = 0.5; +using Clock = std::chrono::steady_clock; +using FSec = std::chrono::duration; + // TODO: Return an oscillating value to make it apparent something was spelled wrong? class UnknownFunctionExpression : public FunctionExpression { @@ -108,8 +111,6 @@ private: const auto now = Clock::now(); const auto elapsed = now - m_start_time; - using FSec = std::chrono::duration; - const ControlState val = GetArg(0).GetValue(); ControlState progress = std::chrono::duration_cast(elapsed).count() / val; @@ -134,7 +135,6 @@ private: std::string GetFuncName() const override { return "timer"; } private: - using Clock = std::chrono::steady_clock; mutable Clock::time_point m_start_time = Clock::now(); }; @@ -207,7 +207,7 @@ class WhileExpression : public FunctionExpression std::string GetFuncName() const override { return "while"; } }; -// usage: deadzone(input, amount) +// usage: !deadzone(input, amount) class DeadzoneExpression : public FunctionExpression { virtual bool ValidateArguments(const std::vector>& args) override @@ -226,7 +226,7 @@ class DeadzoneExpression : public FunctionExpression std::string GetFuncName() const override { return "deadzone"; } }; -// usage: smooth(input, seconds) +// usage: !smooth(input, seconds) // seconds is seconds to change from 0.0 to 1.0 class SmoothExpression : public FunctionExpression { @@ -244,8 +244,6 @@ class SmoothExpression : public FunctionExpression const ControlState desired_value = GetArg(0).GetValue(); const ControlState smooth = GetArg(1).GetValue(); - using FSec = std::chrono::duration; - const ControlState max_move = std::chrono::duration_cast(elapsed).count() / smooth; if (std::isinf(max_move)) @@ -265,8 +263,6 @@ class SmoothExpression : public FunctionExpression std::string GetFuncName() const override { return "smooth"; } private: - using Clock = std::chrono::steady_clock; - mutable ControlState m_value = 0.0; mutable Clock::time_point m_last_update = Clock::now(); }; @@ -294,8 +290,6 @@ class HoldExpression : public FunctionExpression { const auto hold_time = now - m_start_time; - using FSec = std::chrono::duration; - if (std::chrono::duration_cast(hold_time).count() >= GetArg(1).GetValue()) m_state = true; } @@ -307,8 +301,6 @@ class HoldExpression : public FunctionExpression std::string GetFuncName() const override { return "smooth"; } private: - using Clock = std::chrono::steady_clock; - mutable bool m_state = false; mutable Clock::time_point m_start_time = Clock::now(); }; @@ -325,8 +317,6 @@ class TapExpression : public FunctionExpression { const auto now = Clock::now(); - using FSec = std::chrono::duration; - const auto elapsed = std::chrono::duration_cast(now - m_start_time).count(); const ControlState input = GetArg(0).GetValue(); @@ -368,8 +358,6 @@ class TapExpression : public FunctionExpression std::string GetFuncName() const override { return "tap"; } private: - using Clock = std::chrono::steady_clock; - mutable bool m_released = true; mutable u32 m_taps = 0; mutable Clock::time_point m_start_time = Clock::now(); @@ -405,8 +393,6 @@ class RelativeExpression : public FunctionExpression if (GetArgCount() >= 4) m_state = GetArg(3).GetValue(); - using FSec = std::chrono::duration; - const auto elapsed = std::chrono::duration_cast(now - m_last_update).count(); m_last_update = now; @@ -432,8 +418,6 @@ class RelativeExpression : public FunctionExpression std::string GetFuncName() const override { return "relative"; } private: - using Clock = std::chrono::steady_clock; - mutable ControlState m_state = 0.0; mutable Clock::time_point m_last_update = Clock::now(); }; @@ -460,7 +444,6 @@ class PulseExpression : public FunctionExpression { m_released = false; - using FSec = std::chrono::duration; const auto seconds = std::chrono::duration_cast(FSec(GetArg(1).GetValue())); if (m_state) @@ -486,8 +469,6 @@ class PulseExpression : public FunctionExpression std::string GetFuncName() const override { return "pulse"; } private: - using Clock = std::chrono::steady_clock; - mutable bool m_released = false; mutable bool m_state = false; mutable Clock::time_point m_release_time = Clock::now(); -- cgit v1.2.3 From ca7ce674500b7664ed049ac1019a2e40636187dc Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 2 Mar 2019 14:47:26 -0600 Subject: ExpressionParser/DolphinQt: Added parse results to UI. --- .../InputCommon/ControlReference/FunctionExpression.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index 724dddedf0..766df9b5eb 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -17,19 +17,6 @@ constexpr ControlState CONDITION_THRESHOLD = 0.5; using Clock = std::chrono::steady_clock; using FSec = std::chrono::duration; -// TODO: Return an oscillating value to make it apparent something was spelled wrong? -class UnknownFunctionExpression : public FunctionExpression -{ -private: - virtual bool ValidateArguments(const std::vector>& args) override - { - return false; - } - ControlState GetValue() const override { return 0.0; } - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "unknown"; } -}; - // usage: !toggle(toggle_state_input, [clear_state_input]) class ToggleExpression : public FunctionExpression { @@ -503,7 +490,7 @@ std::unique_ptr MakeFunctionExpression(std::string name) else if ("pulse" == name) return std::make_unique(); else - return std::make_unique(); + return nullptr; } int FunctionExpression::CountNumControls() const -- cgit v1.2.3 From 7912dc57ddccc3868dea9f915b1f6d9d4416165d Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Wed, 3 Apr 2019 19:45:09 -0500 Subject: ExpressionParser: Remove !while and add optional 2nd argument to !smooth. --- .../ControlReference/FunctionExpression.cpp | 42 ++++------------------ 1 file changed, 6 insertions(+), 36 deletions(-) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index 766df9b5eb..6c3f10ad74 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -163,37 +163,6 @@ private: std::string GetFuncName() const override { return "minus"; } }; -// usage: !while(condition, expression) -class WhileExpression : public FunctionExpression -{ - virtual bool ValidateArguments(const std::vector>& 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"; } -}; - // usage: !deadzone(input, amount) class DeadzoneExpression : public FunctionExpression { @@ -213,13 +182,13 @@ class DeadzoneExpression : public FunctionExpression std::string GetFuncName() const override { return "deadzone"; } }; -// usage: !smooth(input, seconds) +// usage: !smooth(input, seconds_up, seconds_down = seconds_up) // seconds is seconds to change from 0.0 to 1.0 class SmoothExpression : public FunctionExpression { virtual bool ValidateArguments(const std::vector>& args) override { - return 2 == args.size(); + return 2 == args.size() || 3 == args.size(); } ControlState GetValue() const override @@ -229,8 +198,11 @@ class SmoothExpression : public FunctionExpression m_last_update = now; const ControlState desired_value = GetArg(0).GetValue(); - const ControlState smooth = GetArg(1).GetValue(); + const ControlState smooth_up = GetArg(1).GetValue(); + const ControlState smooth_down = (3 == GetArgCount() ? GetArg(2).GetValue() : smooth_up); + + const ControlState smooth = (desired_value < m_value) ? smooth_down : smooth_up; const ControlState max_move = std::chrono::duration_cast(elapsed).count() / smooth; if (std::isinf(max_move)) @@ -473,8 +445,6 @@ std::unique_ptr MakeFunctionExpression(std::string name) return std::make_unique(); else if ("toggle" == name) return std::make_unique(); - else if ("while" == name) - return std::make_unique(); else if ("minus" == name) return std::make_unique(); else if ("deadzone" == name) -- cgit v1.2.3 From b57178d246640270779d791b00d3a5b8fd7c13f2 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Thu, 4 Apr 2019 17:35:49 -0500 Subject: ExpressionParser: Remove ! character from function syntax. Remove unused serialization functions. --- .../ControlReference/FunctionExpression.cpp | 78 ++++++---------------- 1 file changed, 20 insertions(+), 58 deletions(-) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index 6c3f10ad74..daf36c0fa1 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -17,7 +17,7 @@ constexpr ControlState CONDITION_THRESHOLD = 0.5; using Clock = std::chrono::steady_clock; using FSec = std::chrono::duration; -// usage: !toggle(toggle_state_input, [clear_state_input]) +// usage: toggle(toggle_state_input, [clear_state_input]) class ToggleExpression : public FunctionExpression { private: @@ -49,14 +49,11 @@ private: return m_state; } - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "toggle"; } - mutable bool m_released{}; mutable bool m_state{}; }; -// usage: !not(expression) +// usage: not(expression) class NotExpression : public FunctionExpression { private: @@ -67,10 +64,9 @@ private: 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 ""; } }; -// usage: !sin(expression) +// usage: sin(expression) class SinExpression : public FunctionExpression { private: @@ -80,11 +76,9 @@ private: } ControlState GetValue() const override { return std::sin(GetArg(0).GetValue()); } - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "sin"; } }; -// usage: !timer(seconds) +// usage: timer(seconds) class TimerExpression : public FunctionExpression { private: @@ -118,14 +112,12 @@ private: return progress; } - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "timer"; } private: mutable Clock::time_point m_start_time = Clock::now(); }; -// usage: !if(condition, true_expression, false_expression) +// usage: if(condition, true_expression, false_expression) class IfExpression : public FunctionExpression { private: @@ -139,12 +131,9 @@ private: return (GetArg(0).GetValue() > CONDITION_THRESHOLD) ? GetArg(1).GetValue() : GetArg(2).GetValue(); } - - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "if"; } }; -// usage: !minus(expression) +// usage: minus(expression) class UnaryMinusExpression : public FunctionExpression { private: @@ -158,12 +147,9 @@ private: // Subtraction for clarity: return 0.0 - GetArg(0).GetValue(); } - - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "minus"; } }; -// usage: !deadzone(input, amount) +// usage: deadzone(input, amount) class DeadzoneExpression : public FunctionExpression { virtual bool ValidateArguments(const std::vector>& args) override @@ -177,12 +163,9 @@ class DeadzoneExpression : public FunctionExpression const ControlState deadzone = GetArg(1).GetValue(); return std::copysign(std::max(0.0, std::abs(val) - deadzone) / (1.0 - deadzone), val); } - - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "deadzone"; } }; -// usage: !smooth(input, seconds_up, seconds_down = seconds_up) +// usage: smooth(input, seconds_up, seconds_down = seconds_up) // seconds is seconds to change from 0.0 to 1.0 class SmoothExpression : public FunctionExpression { @@ -218,15 +201,12 @@ class SmoothExpression : public FunctionExpression return m_value; } - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "smooth"; } - private: mutable ControlState m_value = 0.0; mutable Clock::time_point m_last_update = Clock::now(); }; -// usage: !hold(input, seconds) +// usage: hold(input, seconds) class HoldExpression : public FunctionExpression { virtual bool ValidateArguments(const std::vector>& args) override @@ -256,15 +236,12 @@ class HoldExpression : public FunctionExpression return m_state; } - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "smooth"; } - private: mutable bool m_state = false; mutable Clock::time_point m_start_time = Clock::now(); }; -// usage: !tap(input, seconds, taps=2) +// usage: tap(input, seconds, taps=2) class TapExpression : public FunctionExpression { virtual bool ValidateArguments(const std::vector>& args) override @@ -313,16 +290,13 @@ class TapExpression : public FunctionExpression return 0.0; } - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "tap"; } - private: mutable bool m_released = true; mutable u32 m_taps = 0; mutable Clock::time_point m_start_time = Clock::now(); }; -// usage: !relative(input, speed, [max_abs_value, [shared_state]]) +// usage: relative(input, speed, [max_abs_value, [shared_state]]) // speed is max movement per second class RelativeExpression : public FunctionExpression { @@ -337,15 +311,15 @@ class RelativeExpression : public FunctionExpression // // e.g. A single mapping with a relatively adjusted value between 0.0 and 1.0 // Potentially useful for a trigger input - // !relative(`Up` - `Down`, 2.0) + // relative(`Up` - `Down`, 2.0) // // e.g. A value with two mappings (such as analog stick Up/Down) // The shared state allows the two mappings to work together. // This mapping (for up) returns a value clamped between 0.0 and 1.0 - // !relative(`Up`, 2.0, 1.0, $y) + // relative(`Up`, 2.0, 1.0, $y) // This mapping (for down) returns the negative value clamped between 0.0 and 1.0 // (Adjustments created by `Down` are applied negatively to the shared state) - // !relative(`Down`, 2.0, -1.0, $y) + // relative(`Down`, 2.0, -1.0, $y) const auto now = Clock::now(); @@ -373,15 +347,12 @@ class RelativeExpression : public FunctionExpression return std::max(0.0, m_state * std::copysign(1.0, max_abs_value)); } - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "relative"; } - private: mutable ControlState m_state = 0.0; mutable Clock::time_point m_last_update = Clock::now(); }; -// usage: !pulse(input, seconds) +// usage: pulse(input, seconds) class PulseExpression : public FunctionExpression { virtual bool ValidateArguments(const std::vector>& args) override @@ -424,9 +395,6 @@ class PulseExpression : public FunctionExpression return m_state; } - void SetValue(ControlState value) override {} - std::string GetFuncName() const override { return "pulse"; } - private: mutable bool m_released = false; mutable bool m_state = false; @@ -435,7 +403,7 @@ private: std::unique_ptr MakeFunctionExpression(std::string name) { - if (name.empty()) + if ("not" == name) return std::make_unique(); else if ("if" == name) return std::make_unique(); @@ -479,16 +447,6 @@ void FunctionExpression::UpdateReferences(ControlEnvironment& env) arg->UpdateReferences(env); } -FunctionExpression::operator std::string() const -{ - std::string result = '!' + GetFuncName(); - - for (auto& arg : m_args) - result += ' ' + static_cast(*arg); - - return result; -} - bool FunctionExpression::SetArguments(std::vector>&& args) { m_args = std::move(args); @@ -511,5 +469,9 @@ u32 FunctionExpression::GetArgCount() const return u32(m_args.size()); } +void FunctionExpression::SetValue(ControlState) +{ +} + } // namespace ExpressionParser } // namespace ciface -- cgit v1.2.3 From 4d41bd64c8c01884fa0c2a663cc76a696c30adbb Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Fri, 11 Oct 2019 19:38:18 -0500 Subject: ExpressionParser: Show error message with expected arguments. --- .../ControlReference/FunctionExpression.cpp | 99 ++++++++++++++++------ 1 file changed, 74 insertions(+), 25 deletions(-) (limited to 'Source/Core/InputCommon/ControlReference/FunctionExpression.cpp') diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp index daf36c0fa1..18b98c5e19 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.cpp @@ -21,10 +21,14 @@ using FSec = std::chrono::duration; class ToggleExpression : public FunctionExpression { private: - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { // Optional 2nd argument for clearing state: - return 1 == args.size() || 2 == args.size(); + if (1 == args.size() || 2 == args.size()) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"toggle_state_input, [clear_state_input]"}; } ControlState GetValue() const override @@ -57,9 +61,13 @@ private: class NotExpression : public FunctionExpression { private: - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { - return 1 == args.size(); + if (1 == args.size()) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"expression"}; } ControlState GetValue() const override { return 1.0 - GetArg(0).GetValue(); } @@ -70,9 +78,13 @@ private: class SinExpression : public FunctionExpression { private: - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { - return 1 == args.size(); + if (1 == args.size()) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"expression"}; } ControlState GetValue() const override { return std::sin(GetArg(0).GetValue()); } @@ -82,9 +94,13 @@ private: class TimerExpression : public FunctionExpression { private: - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { - return 1 == args.size(); + if (1 == args.size()) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"seconds"}; } ControlState GetValue() const override @@ -121,9 +137,13 @@ private: class IfExpression : public FunctionExpression { private: - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { - return 3 == args.size(); + if (3 == args.size()) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"condition, true_expression, false_expression"}; } ControlState GetValue() const override @@ -137,9 +157,13 @@ private: class UnaryMinusExpression : public FunctionExpression { private: - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { - return 1 == args.size(); + if (1 == args.size()) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"expression"}; } ControlState GetValue() const override @@ -152,9 +176,13 @@ private: // usage: deadzone(input, amount) class DeadzoneExpression : public FunctionExpression { - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { - return 2 == args.size(); + if (2 == args.size()) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"input, amount"}; } ControlState GetValue() const override @@ -169,9 +197,13 @@ class DeadzoneExpression : public FunctionExpression // seconds is seconds to change from 0.0 to 1.0 class SmoothExpression : public FunctionExpression { - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { - return 2 == args.size() || 3 == args.size(); + if (2 == args.size() || 3 == args.size()) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"input, seconds_up, seconds_down = seconds_up"}; } ControlState GetValue() const override @@ -209,9 +241,13 @@ private: // usage: hold(input, seconds) class HoldExpression : public FunctionExpression { - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { - return 2 == args.size(); + if (2 == args.size()) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"input, seconds"}; } ControlState GetValue() const override @@ -244,9 +280,13 @@ private: // usage: tap(input, seconds, taps=2) class TapExpression : public FunctionExpression { - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { - return 2 == args.size() || 3 == args.size(); + if (2 == args.size() || 3 == args.size()) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"input, seconds, taps = 2"}; } ControlState GetValue() const override @@ -300,9 +340,13 @@ private: // speed is max movement per second class RelativeExpression : public FunctionExpression { - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { - return args.size() >= 2 && args.size() <= 4; + if (args.size() >= 2 && args.size() <= 4) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"input, speed, [max_abs_value, [shared_state]]"}; } ControlState GetValue() const override @@ -355,9 +399,13 @@ private: // usage: pulse(input, seconds) class PulseExpression : public FunctionExpression { - virtual bool ValidateArguments(const std::vector>& args) override + ArgumentValidation + ValidateArguments(const std::vector>& args) override { - return 2 == args.size(); + if (2 == args.size()) + return ArgumentsAreValid{}; + else + return ExpectedArguments{"input, seconds"}; } ControlState GetValue() const override @@ -447,7 +495,8 @@ void FunctionExpression::UpdateReferences(ControlEnvironment& env) arg->UpdateReferences(env); } -bool FunctionExpression::SetArguments(std::vector>&& args) +FunctionExpression::ArgumentValidation +FunctionExpression::SetArguments(std::vector>&& args) { m_args = std::move(args); -- cgit v1.2.3