diff --git a/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp b/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp index aacaa3ae89..16058c2aa2 100644 --- a/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp @@ -81,7 +81,7 @@ QTextCharFormat GetVariableCharFormat() return format; } -QTextCharFormat GetFunctionCharFormat() +QTextCharFormat GetBarewordCharFormat() { QTextCharFormat format; format.setForeground(QBrush{Qt::darkCyan}); @@ -125,8 +125,8 @@ void ControlExpressionSyntaxHighlighter::highlightBlock(const QString& text) case TokenType::TOK_CONTROL: char_format = GetControlCharFormat(); break; - case TokenType::TOK_FUNCTION: - char_format = GetFunctionCharFormat(); + case TokenType::TOK_BAREWORD: + char_format = GetBarewordCharFormat(); break; case TokenType::TOK_VARIABLE: char_format = GetVariableCharFormat(); @@ -220,16 +220,15 @@ void IOWindow::CreateMainLayout() m_functions_combo = new QComboBox(); m_functions_combo->addItem(tr("Functions")); m_functions_combo->insertSeparator(1); - m_functions_combo->addItem(QStringLiteral("!if")); - m_functions_combo->addItem(QStringLiteral("!sin")); - m_functions_combo->addItem(QStringLiteral("!timer")); - m_functions_combo->addItem(QStringLiteral("!toggle")); - m_functions_combo->addItem(QStringLiteral("!deadzone")); - m_functions_combo->addItem(QStringLiteral("!smooth")); - m_functions_combo->addItem(QStringLiteral("!hold")); - m_functions_combo->addItem(QStringLiteral("!tap")); - m_functions_combo->addItem(QStringLiteral("!relative")); - m_functions_combo->addItem(QStringLiteral("!pulse")); + m_functions_combo->addItem(QStringLiteral("if")); + m_functions_combo->addItem(QStringLiteral("timer")); + m_functions_combo->addItem(QStringLiteral("toggle")); + m_functions_combo->addItem(QStringLiteral("deadzone")); + m_functions_combo->addItem(QStringLiteral("smooth")); + m_functions_combo->addItem(QStringLiteral("hold")); + m_functions_combo->addItem(QStringLiteral("tap")); + m_functions_combo->addItem(QStringLiteral("relative")); + m_functions_combo->addItem(QStringLiteral("pulse")); // Devices m_main_layout->addWidget(m_devices_combo); diff --git a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp index a686e8fcc4..f7937a19bd 100644 --- a/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp +++ b/Source/Core/InputCommon/ControlReference/ExpressionParser.cpp @@ -21,42 +21,6 @@ namespace ciface::ExpressionParser { using namespace ciface::Core; -inline std::string OpName(TokenType op) -{ - switch (op) - { - case TOK_AND: - return "And"; - case TOK_OR: - return "Or"; - case TOK_FUNCTION: - return "Function"; - case TOK_ADD: - return "Add"; - case TOK_SUB: - return "Sub"; - case TOK_MUL: - return "Mul"; - case TOK_DIV: - return "Div"; - case TOK_MOD: - return "Mod"; - case TOK_ASSIGN: - return "Assign"; - case TOK_LTHAN: - return "LThan"; - case TOK_GTHAN: - return "GThan"; - case TOK_COMMA: - return "Comma"; - case TOK_VARIABLE: - return "Var"; - default: - assert(false); - return ""; - } -} - Token::Token(TokenType type_) : type(type_) { } @@ -70,55 +34,6 @@ bool Token::IsBinaryOperator() const return type >= TOK_BINARY_OPS_BEGIN && type < TOK_BINARY_OPS_END; } -Token::operator std::string() const -{ - switch (type) - { - case TOK_DISCARD: - return "Discard"; - case TOK_EOF: - return "EOF"; - case TOK_LPAREN: - return "("; - case TOK_RPAREN: - return ")"; - case TOK_AND: - return "&"; - case TOK_OR: - return "|"; - case TOK_FUNCTION: - return '!' + data; - case TOK_ADD: - return "+"; - case TOK_SUB: - return "-"; - case TOK_MUL: - return "*"; - case TOK_DIV: - return "/"; - case TOK_MOD: - return "%"; - case TOK_ASSIGN: - return "="; - case TOK_LTHAN: - return "<"; - case TOK_GTHAN: - return ">"; - case TOK_COMMA: - return ","; - case TOK_CONTROL: - return "Device(" + data + ')'; - case TOK_LITERAL: - return '\'' + data + '\''; - case TOK_VARIABLE: - return '$' + data; - default: - break; - } - - return "Invalid"; -} - Lexer::Lexer(const std::string& expr_) : expr(expr_) { it = expr.begin(); @@ -134,21 +49,12 @@ std::string Lexer::FetchDelimString(char delim) std::string Lexer::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(R"([a-z\d_])", std::regex_constants::icase); return FetchCharsWhile([&rx](char c) { return std::regex_match(std::string(1, c), rx); }); } -Token Lexer::GetFunction() -{ - return Token(TOK_FUNCTION, FetchWordChars()); -} - Token Lexer::GetDelimitedLiteral() { return Token(TOK_LITERAL, FetchDelimString('\'')); @@ -164,21 +70,15 @@ Token Lexer::GetFullyQualifiedControl() return Token(TOK_CONTROL, FetchDelimString('`')); } -Token Lexer::GetBarewordsControl(char c) +Token Lexer::GetBareword(char first_char) { - std::string name; - name += c; - name += FetchCharsWhile([](char c) { return std::isalpha(c, std::locale::classic()); }); - - ControlQualifier qualifier; - qualifier.control_name = name; - return Token(TOK_CONTROL, qualifier); + return Token(TOK_BAREWORD, first_char + FetchWordChars()); } -Token Lexer::GetRealLiteral(char c) +Token Lexer::GetRealLiteral(char first_char) { std::string value; - value += c; + value += first_char; value += FetchCharsWhile([](char c) { return isdigit(c, std::locale::classic()) || ('.' == c); }); if (std::regex_match(value, std::regex(R"(\d+(\.\d+)?)"))) @@ -209,7 +109,7 @@ Token Lexer::NextToken() case '|': return Token(TOK_OR); case '!': - return GetFunction(); + return Token(TOK_NOT); case '+': return Token(TOK_ADD); case '-': @@ -236,7 +136,7 @@ Token Lexer::NextToken() return GetFullyQualifiedControl(); default: if (isalpha(c, std::locale::classic())) - return GetBarewordsControl(c); + return GetBareword(c); else if (isdigit(c, std::locale::classic())) return GetRealLiteral(c); else @@ -300,7 +200,6 @@ public: input = env.FindInput(qualifier); output = env.FindOutput(qualifier); } - operator std::string() const override { return "`" + static_cast(qualifier) + "`"; } private: ControlQualifier qualifier; @@ -384,11 +283,6 @@ public: lhs->UpdateReferences(env); rhs->UpdateReferences(env); } - - operator std::string() const override - { - return OpName(op) + "(" + std::string(*lhs) + ", " + std::string(*rhs) + ")"; - } }; class LiteralExpression : public Expression @@ -406,8 +300,6 @@ public: // Nothing needed. } - operator std::string() const override { return '\'' + GetName() + '\''; } - protected: virtual std::string GetName() const = 0; }; @@ -450,8 +342,6 @@ public: m_value_ptr = env.GetVariablePtr(m_name); } - operator std::string() const override { return '$' + m_name; } - protected: const std::string m_name; ControlState* m_value_ptr{}; @@ -471,12 +361,6 @@ public: void SetValue(ControlState value) override { GetActiveChild()->SetValue(value); } int CountNumControls() const override { return GetActiveChild()->CountNumControls(); } - operator std::string() const override - { - return "Coalesce(" + static_cast(*m_lhs) + ", " + - static_cast(*m_rhs) + ')'; - } - void UpdateReferences(ControlEnvironment& env) override { m_lhs->UpdateReferences(env); @@ -566,19 +450,6 @@ public: } private: - struct FunctionArguments - { - FunctionArguments(ParseResult&& result_, std::vector>&& args_ = {}) - : result(std::move(result_)), args(std::move(args_)) - { - } - - // Note: expression member isn't being used. - ParseResult result; - - std::vector> args; - }; - const std::vector& tokens; std::vector::const_iterator m_it; @@ -598,75 +469,81 @@ private: return tok.type == type; } - FunctionArguments ParseFunctionArguments() + ParseResult ParseFunctionArguments(std::unique_ptr&& func, + const Token& func_tok) { std::vector> args; if (TOK_LPAREN != Peek().type) { // Single argument with no parens (useful for unary ! function) - auto arg = ParseAtom(Chew()); + const auto tok = Chew(); + auto arg = ParseAtom(tok); if (ParseStatus::Successful != arg.status) - return {std::move(arg)}; + return arg; args.emplace_back(std::move(arg.expr)); - return {ParseResult::MakeSuccessfulResult({}), std::move(args)}; } - - // Chew the L-Paren - Chew(); - - // Check for empty argument list: - if (TOK_RPAREN == Peek().type) + else { + // Chew the L-Paren Chew(); - return {ParseResult::MakeSuccessfulResult({})}; + + // Check for empty argument list: + if (TOK_RPAREN == Peek().type) + { + Chew(); + } + else + { + while (true) + { + // Read one argument. + // Grab an expression, but stop at comma. + auto arg = ParseBinary(BinaryOperatorPrecedence(TOK_COMMA)); + if (ParseStatus::Successful != arg.status) + return arg; + + args.emplace_back(std::move(arg.expr)); + + // Right paren is the end of our arguments. + const Token tok = Chew(); + if (TOK_RPAREN == tok.type) + break; + + // Comma before the next argument. + if (TOK_COMMA != tok.type) + return ParseResult::MakeErrorResult(tok, _trans("Expected comma.")); + }; + } } - while (true) + if (!func->SetArguments(std::move(args))) { - // Read one argument. - // Grab an expression, but stop at comma. - auto arg = ParseBinary(BinaryOperatorPrecedence(TOK_COMMA)); - if (ParseStatus::Successful != arg.status) - return {std::move(arg)}; - - args.emplace_back(std::move(arg.expr)); - - // Right paren is the end of our arguments. - const Token tok = Chew(); - if (TOK_RPAREN == tok.type) - return {ParseResult::MakeSuccessfulResult({}), std::move(args)}; - - // Comma before the next argument. - if (TOK_COMMA != tok.type) - return {ParseResult::MakeErrorResult(tok, _trans("Expected comma."))}; + // TODO: It would be nice to output how many arguments are expected. + return ParseResult::MakeErrorResult(func_tok, _trans("Wrong number of arguments.")); } + + return ParseResult::MakeSuccessfulResult(std::move(func)); } ParseResult ParseAtom(const Token& tok) { switch (tok.type) { - case TOK_FUNCTION: + case TOK_BAREWORD: { auto func = MakeFunctionExpression(tok.data); if (!func) - return ParseResult::MakeErrorResult(tok, _trans("Unknown function.")); - - auto args = ParseFunctionArguments(); - - if (ParseStatus::Successful != args.result.status) - return std::move(args.result); - - if (!func->SetArguments(std::move(args.args))) { - // TODO: It would be nice to output how many arguments are expected. - return ParseResult::MakeErrorResult(tok, _trans("Wrong number of arguments.")); + // Invalid function, interpret this as a bareword control. + Token control_tok(tok); + control_tok.type = TOK_CONTROL; + return ParseAtom(control_tok); } - return ParseResult::MakeSuccessfulResult(std::move(func)); + return ParseFunctionArguments(std::move(func), tok); } case TOK_CONTROL: { @@ -674,6 +551,10 @@ private: cq.FromString(tok.data); return ParseResult::MakeSuccessfulResult(std::make_unique(cq)); } + case TOK_NOT: + { + return ParseFunctionArguments(MakeFunctionExpression("not"), tok); + } case TOK_LITERAL: { return MakeLiteralExpression(tok); @@ -690,16 +571,13 @@ private: { // An atom was expected but we got a subtraction symbol. // Interpret it as a unary minus function. - - // Make sure to copy the existing string position values for proper error results. - Token func = tok; - func.type = TOK_FUNCTION; - func.data = "minus"; - return ParseAtom(std::move(func)); + return ParseFunctionArguments(MakeFunctionExpression("minus"), tok); } default: + { return ParseResult::MakeErrorResult(tok, _trans("Expected start of expression.")); } + } } static int BinaryOperatorPrecedence(TokenType type) diff --git a/Source/Core/InputCommon/ControlReference/ExpressionParser.h b/Source/Core/InputCommon/ControlReference/ExpressionParser.h index 3dac67e9db..2f41d2fde7 100644 --- a/Source/Core/InputCommon/ControlReference/ExpressionParser.h +++ b/Source/Core/InputCommon/ControlReference/ExpressionParser.h @@ -20,10 +20,11 @@ enum TokenType TOK_EOF, TOK_LPAREN, TOK_RPAREN, - TOK_FUNCTION, + TOK_NOT, TOK_CONTROL, TOK_LITERAL, TOK_VARIABLE, + TOK_BAREWORD, // Binary Ops: TOK_BINARY_OPS_BEGIN, TOK_AND = TOK_BINARY_OPS_BEGIN, @@ -54,7 +55,6 @@ public: Token(TokenType type_, std::string data_); bool IsBinaryOperator() const; - operator std::string() const; }; enum class ParseStatus @@ -89,11 +89,10 @@ private: std::string FetchDelimString(char delim); std::string FetchWordChars(); - Token GetFunction(); Token GetDelimitedLiteral(); Token GetVariable(); Token GetFullyQualifiedControl(); - Token GetBarewordsControl(char c); + Token GetBareword(char c); Token GetRealLiteral(char c); Token NextToken(); @@ -164,7 +163,6 @@ public: virtual void SetValue(ControlState state) = 0; virtual int CountNumControls() const = 0; virtual void UpdateReferences(ControlEnvironment& finder) = 0; - virtual operator std::string() const = 0; }; class ParseResult 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 diff --git a/Source/Core/InputCommon/ControlReference/FunctionExpression.h b/Source/Core/InputCommon/ControlReference/FunctionExpression.h index 35e000a16f..14b438a299 100644 --- a/Source/Core/InputCommon/ControlReference/FunctionExpression.h +++ b/Source/Core/InputCommon/ControlReference/FunctionExpression.h @@ -20,12 +20,12 @@ class FunctionExpression : public Expression public: int CountNumControls() const override; void UpdateReferences(ControlEnvironment& env) override; - operator std::string() const override; bool SetArguments(std::vector>&& args); + void SetValue(ControlState value) override; + protected: - virtual std::string GetFuncName() const = 0; virtual bool ValidateArguments(const std::vector>& args) = 0; Expression& GetArg(u32 number);