1 // Copyright 2013 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include <algorithm>
6 #include <cassert>
7 #include <cmath>
8 #include <iostream>
9 #include <map>
10 #include <memory>
11 #include <regex>
12 #include <string>
13 #include <utility>
14 #include <vector>
15
16 #include "Common/Common.h"
17 #include "Common/StringUtil.h"
18
19 #include "InputCommon/ControlReference/ExpressionParser.h"
20 #include "InputCommon/ControlReference/FunctionExpression.h"
21
22 namespace ciface::ExpressionParser
23 {
24 using namespace ciface::Core;
25
26 class ControlExpression;
27
28 class HotkeySuppressions
29 {
30 public:
31 using Modifiers = std::vector<std::unique_ptr<ControlExpression>>;
32
33 struct InvokingDeleter
34 {
35 template <typename T>
operator ()ciface::ExpressionParser::HotkeySuppressions::InvokingDeleter36 void operator()(T* func)
37 {
38 (*func)();
39 }
40 };
41
42 using Suppressor = std::unique_ptr<std::function<void()>, InvokingDeleter>;
43
IsSuppressed(Device::Input * input) const44 bool IsSuppressed(Device::Input* input) const
45 {
46 // Input is suppressed if it exists in the map at all.
47 return m_suppressions.lower_bound({input, nullptr}) !=
48 m_suppressions.lower_bound({input + 1, nullptr});
49 }
50
51 bool IsSuppressedIgnoringModifiers(Device::Input* input, const Modifiers& ignore_modifiers) const;
52
53 // Suppresses each input + modifier pair.
54 // The returned object removes the suppression on destruction.
55 Suppressor MakeSuppressor(const Modifiers* modifiers,
56 const std::unique_ptr<ControlExpression>* final_input);
57
58 private:
59 using Suppression = std::pair<Device::Input*, Device::Input*>;
60 using SuppressionLevel = u16;
61
RemoveSuppression(Device::Input * modifier,Device::Input * final_input)62 void RemoveSuppression(Device::Input* modifier, Device::Input* final_input)
63 {
64 auto it = m_suppressions.find({final_input, modifier});
65 if ((--it->second) == 0)
66 m_suppressions.erase(it);
67 }
68
69 // Holds counts of suppressions for each input/modifier pair.
70 std::map<Suppression, SuppressionLevel> m_suppressions;
71 };
72
73 static HotkeySuppressions s_hotkey_suppressions;
74
Token(TokenType type_)75 Token::Token(TokenType type_) : type(type_)
76 {
77 }
78
Token(TokenType type_,std::string data_)79 Token::Token(TokenType type_, std::string data_) : type(type_), data(std::move(data_))
80 {
81 }
82
IsBinaryOperator() const83 bool Token::IsBinaryOperator() const
84 {
85 return type >= TOK_BINARY_OPS_BEGIN && type < TOK_BINARY_OPS_END;
86 }
87
Lexer(std::string expr_)88 Lexer::Lexer(std::string expr_) : expr(std::move(expr_))
89 {
90 it = expr.begin();
91 }
92
FetchDelimString(char delim)93 std::string Lexer::FetchDelimString(char delim)
94 {
95 const std::string result = FetchCharsWhile([delim](char c) { return c != delim; });
96 if (it != expr.end())
97 ++it;
98 return result;
99 }
100
FetchWordChars()101 std::string Lexer::FetchWordChars()
102 {
103 // Valid word characters:
104 std::regex rx(R"([a-z\d_])", std::regex_constants::icase);
105
106 return FetchCharsWhile([&rx](char c) { return std::regex_match(std::string(1, c), rx); });
107 }
108
GetDelimitedLiteral()109 Token Lexer::GetDelimitedLiteral()
110 {
111 return Token(TOK_LITERAL, FetchDelimString('\''));
112 }
113
GetVariable()114 Token Lexer::GetVariable()
115 {
116 return Token(TOK_VARIABLE, FetchWordChars());
117 }
118
GetFullyQualifiedControl()119 Token Lexer::GetFullyQualifiedControl()
120 {
121 return Token(TOK_CONTROL, FetchDelimString('`'));
122 }
123
GetBareword(char first_char)124 Token Lexer::GetBareword(char first_char)
125 {
126 return Token(TOK_BAREWORD, first_char + FetchWordChars());
127 }
128
GetRealLiteral(char first_char)129 Token Lexer::GetRealLiteral(char first_char)
130 {
131 std::string value;
132 value += first_char;
133 value += FetchCharsWhile([](char c) { return isdigit(c, std::locale::classic()) || ('.' == c); });
134
135 if (std::regex_match(value, std::regex(R"(\d+(\.\d+)?)")))
136 return Token(TOK_LITERAL, value);
137
138 return Token(TOK_INVALID);
139 }
140
PeekToken()141 Token Lexer::PeekToken()
142 {
143 const auto old_it = it;
144 const auto tok = NextToken();
145 it = old_it;
146 return tok;
147 }
148
NextToken()149 Token Lexer::NextToken()
150 {
151 if (it == expr.end())
152 return Token(TOK_EOF);
153
154 char c = *it++;
155 switch (c)
156 {
157 case ' ':
158 case '\t':
159 case '\n':
160 case '\r':
161 return Token(TOK_WHITESPACE);
162 case '(':
163 return Token(TOK_LPAREN);
164 case ')':
165 return Token(TOK_RPAREN);
166 case '@':
167 return Token(TOK_HOTKEY);
168 case '&':
169 return Token(TOK_AND);
170 case '|':
171 return Token(TOK_OR);
172 case '!':
173 return Token(TOK_NOT);
174 case '+':
175 return Token(TOK_ADD);
176 case '-':
177 return Token(TOK_SUB);
178 case '*':
179 return Token(TOK_MUL);
180 case '/':
181 return Token(TOK_DIV);
182 case '%':
183 return Token(TOK_MOD);
184 case '=':
185 return Token(TOK_ASSIGN);
186 case '<':
187 return Token(TOK_LTHAN);
188 case '>':
189 return Token(TOK_GTHAN);
190 case ',':
191 return Token(TOK_COMMA);
192 case '^':
193 return Token(TOK_XOR);
194 case '\'':
195 return GetDelimitedLiteral();
196 case '$':
197 return GetVariable();
198 case '`':
199 return GetFullyQualifiedControl();
200 default:
201 if (isalpha(c, std::locale::classic()))
202 return GetBareword(c);
203 else if (isdigit(c, std::locale::classic()))
204 return GetRealLiteral(c);
205 else
206 return Token(TOK_INVALID);
207 }
208 }
209
Tokenize(std::vector<Token> & tokens)210 ParseStatus Lexer::Tokenize(std::vector<Token>& tokens)
211 {
212 while (true)
213 {
214 const std::size_t string_position = it - expr.begin();
215 Token tok = NextToken();
216
217 tok.string_position = string_position;
218 tok.string_length = it - expr.begin();
219
220 // Handle /* */ style comments.
221 if (tok.type == TOK_DIV && PeekToken().type == TOK_MUL)
222 {
223 const auto end_of_comment = expr.find("*/", it - expr.begin());
224
225 if (end_of_comment == std::string::npos)
226 return ParseStatus::SyntaxError;
227
228 tok.type = TOK_COMMENT;
229 tok.string_length = end_of_comment + 4;
230
231 it = expr.begin() + end_of_comment + 2;
232 }
233
234 tokens.push_back(tok);
235
236 if (tok.type == TOK_INVALID)
237 return ParseStatus::SyntaxError;
238
239 if (tok.type == TOK_EOF)
240 break;
241 }
242 return ParseStatus::Successful;
243 }
244
245 class ControlExpression : public Expression
246 {
247 public:
248 // Keep a shared_ptr to the device so the control pointer doesn't become invalid.
249 std::shared_ptr<Device> m_device;
250
ControlExpression(ControlQualifier qualifier_)251 explicit ControlExpression(ControlQualifier qualifier_) : qualifier(qualifier_) {}
252
GetValue() const253 ControlState GetValue() const override
254 {
255 if (s_hotkey_suppressions.IsSuppressed(input))
256 return 0;
257 else
258 return GetValueIgnoringSuppression();
259 }
260
GetValueIgnoringSuppression() const261 ControlState GetValueIgnoringSuppression() const
262 {
263 if (!input)
264 return 0.0;
265
266 // Note: Inputs may return negative values in situations where opposing directions are
267 // activated. We clamp off the negative values here.
268
269 // FYI: Clamping values greater than 1.0 is purposely not done to support unbounded values in
270 // the future. (e.g. raw accelerometer/gyro data)
271
272 return std::max(0.0, input->GetState());
273 }
SetValue(ControlState value)274 void SetValue(ControlState value) override
275 {
276 if (output)
277 output->SetState(value);
278 }
CountNumControls() const279 int CountNumControls() const override { return (input || output) ? 1 : 0; }
UpdateReferences(ControlEnvironment & env)280 void UpdateReferences(ControlEnvironment& env) override
281 {
282 m_device = env.FindDevice(qualifier);
283 input = env.FindInput(qualifier);
284 output = env.FindOutput(qualifier);
285 }
286
GetInput() const287 Device::Input* GetInput() const { return input; };
288
289 private:
290 ControlQualifier qualifier;
291 Device::Input* input = nullptr;
292 Device::Output* output = nullptr;
293 };
294
IsSuppressedIgnoringModifiers(Device::Input * input,const Modifiers & ignore_modifiers) const295 bool HotkeySuppressions::IsSuppressedIgnoringModifiers(Device::Input* input,
296 const Modifiers& ignore_modifiers) const
297 {
298 // Input is suppressed if it exists in the map with a modifier that we aren't ignoring.
299 auto it = m_suppressions.lower_bound({input, nullptr});
300 auto it_end = m_suppressions.lower_bound({input + 1, nullptr});
301
302 // We need to ignore L_Ctrl R_Ctrl when supplied Ctrl and vice-versa.
303 const auto is_same_modifier = [](Device::Input* i1, Device::Input* i2) {
304 return i1 == i2 || i1->IsChild(i2) || i2->IsChild(i1);
305 };
306
307 return std::any_of(it, it_end, [&](auto& s) {
308 return std::none_of(begin(ignore_modifiers), end(ignore_modifiers),
309 [&](auto& m) { return is_same_modifier(m->GetInput(), s.first.second); });
310 });
311 }
312
313 HotkeySuppressions::Suppressor
MakeSuppressor(const Modifiers * modifiers,const std::unique_ptr<ControlExpression> * final_input)314 HotkeySuppressions::MakeSuppressor(const Modifiers* modifiers,
315 const std::unique_ptr<ControlExpression>* final_input)
316 {
317 for (auto& modifier : *modifiers)
318 ++m_suppressions[{(*final_input)->GetInput(), modifier->GetInput()}];
319
320 return Suppressor(std::make_unique<std::function<void()>>([this, modifiers, final_input]() {
321 for (auto& modifier : *modifiers)
322 RemoveSuppression(modifier->GetInput(), (*final_input)->GetInput());
323 }).release(),
324 InvokingDeleter{});
325 }
326
327 class BinaryExpression : public Expression
328 {
329 public:
330 TokenType op;
331 std::unique_ptr<Expression> lhs;
332 std::unique_ptr<Expression> rhs;
333
BinaryExpression(TokenType op_,std::unique_ptr<Expression> && lhs_,std::unique_ptr<Expression> && rhs_)334 BinaryExpression(TokenType op_, std::unique_ptr<Expression>&& lhs_,
335 std::unique_ptr<Expression>&& rhs_)
336 : op(op_), lhs(std::move(lhs_)), rhs(std::move(rhs_))
337 {
338 }
339
GetValue() const340 ControlState GetValue() const override
341 {
342 switch (op)
343 {
344 case TOK_AND:
345 return std::min(lhs->GetValue(), rhs->GetValue());
346 case TOK_OR:
347 return std::max(lhs->GetValue(), rhs->GetValue());
348 case TOK_ADD:
349 return lhs->GetValue() + rhs->GetValue();
350 case TOK_SUB:
351 return lhs->GetValue() - rhs->GetValue();
352 case TOK_MUL:
353 return lhs->GetValue() * rhs->GetValue();
354 case TOK_DIV:
355 {
356 const ControlState result = lhs->GetValue() / rhs->GetValue();
357 return std::isinf(result) ? 0.0 : result;
358 }
359 case TOK_MOD:
360 {
361 const ControlState result = std::fmod(lhs->GetValue(), rhs->GetValue());
362 return std::isnan(result) ? 0.0 : result;
363 }
364 case TOK_ASSIGN:
365 {
366 lhs->SetValue(rhs->GetValue());
367 return lhs->GetValue();
368 }
369 case TOK_LTHAN:
370 return lhs->GetValue() < rhs->GetValue();
371 case TOK_GTHAN:
372 return lhs->GetValue() > rhs->GetValue();
373 case TOK_COMMA:
374 {
375 // Eval and discard lhs:
376 lhs->GetValue();
377 return rhs->GetValue();
378 }
379 case TOK_XOR:
380 {
381 const auto lval = lhs->GetValue();
382 const auto rval = rhs->GetValue();
383 return std::max(std::min(1 - lval, rval), std::min(lval, 1 - rval));
384 }
385 default:
386 assert(false);
387 return 0;
388 }
389 }
390
SetValue(ControlState value)391 void SetValue(ControlState value) override
392 {
393 // Don't do anything special with the op we have.
394 // Treat "A & B" the same as "A | B".
395 lhs->SetValue(value);
396 rhs->SetValue(value);
397 }
398
CountNumControls() const399 int CountNumControls() const override
400 {
401 return lhs->CountNumControls() + rhs->CountNumControls();
402 }
403
UpdateReferences(ControlEnvironment & env)404 void UpdateReferences(ControlEnvironment& env) override
405 {
406 lhs->UpdateReferences(env);
407 rhs->UpdateReferences(env);
408 }
409 };
410
411 class LiteralExpression : public Expression
412 {
413 public:
SetValue(ControlState)414 void SetValue(ControlState) override
415 {
416 // Do nothing.
417 }
418
CountNumControls() const419 int CountNumControls() const override { return 1; }
420
UpdateReferences(ControlEnvironment &)421 void UpdateReferences(ControlEnvironment&) override
422 {
423 // Nothing needed.
424 }
425
426 protected:
427 virtual std::string GetName() const = 0;
428 };
429
430 class LiteralReal : public LiteralExpression
431 {
432 public:
LiteralReal(ControlState value)433 LiteralReal(ControlState value) : m_value(value) {}
434
GetValue() const435 ControlState GetValue() const override { return m_value; }
436
GetName() const437 std::string GetName() const override { return ValueToString(m_value); }
438
439 private:
440 const ControlState m_value{};
441 };
442
MakeLiteralExpression(Token token)443 static ParseResult MakeLiteralExpression(Token token)
444 {
445 ControlState val{};
446 if (TryParse(token.data, &val))
447 return ParseResult::MakeSuccessfulResult(std::make_unique<LiteralReal>(val));
448 else
449 return ParseResult::MakeErrorResult(token, _trans("Invalid literal."));
450 }
451
452 class VariableExpression : public Expression
453 {
454 public:
VariableExpression(std::string name)455 VariableExpression(std::string name) : m_name(name) {}
456
GetValue() const457 ControlState GetValue() const override { return *m_value_ptr; }
458
SetValue(ControlState value)459 void SetValue(ControlState value) override { *m_value_ptr = value; }
460
CountNumControls() const461 int CountNumControls() const override { return 1; }
462
UpdateReferences(ControlEnvironment & env)463 void UpdateReferences(ControlEnvironment& env) override
464 {
465 m_value_ptr = env.GetVariablePtr(m_name);
466 }
467
468 protected:
469 const std::string m_name;
470 ControlState* m_value_ptr{};
471 };
472
473 class HotkeyExpression : public Expression
474 {
475 public:
HotkeyExpression(std::vector<std::unique_ptr<ControlExpression>> inputs)476 HotkeyExpression(std::vector<std::unique_ptr<ControlExpression>> inputs)
477 : m_modifiers(std::move(inputs))
478 {
479 m_final_input = std::move(m_modifiers.back());
480 m_modifiers.pop_back();
481 }
482
GetValue() const483 ControlState GetValue() const override
484 {
485 const bool modifiers_pressed = std::all_of(m_modifiers.begin(), m_modifiers.end(),
486 [](const std::unique_ptr<ControlExpression>& input) {
487 return input->GetValue() > CONDITION_THRESHOLD;
488 });
489
490 const auto final_input_state = m_final_input->GetValueIgnoringSuppression();
491
492 if (modifiers_pressed)
493 {
494 // Ignore suppression of our own modifiers. This also allows superset modifiers to function.
495 const bool is_suppressed = s_hotkey_suppressions.IsSuppressedIgnoringModifiers(
496 m_final_input->GetInput(), m_modifiers);
497
498 if (final_input_state < CONDITION_THRESHOLD)
499 m_is_blocked = false;
500
501 // If some other hotkey suppressed us, require a release of final input to be ready again.
502 if (is_suppressed)
503 m_is_blocked = true;
504
505 if (m_is_blocked)
506 return 0;
507
508 EnableSuppression();
509
510 // Our modifiers are active. Pass through the final input.
511 return final_input_state;
512 }
513 else
514 {
515 m_suppressor = {};
516 m_is_blocked = final_input_state > CONDITION_THRESHOLD;
517 }
518
519 return 0;
520 }
521
SetValue(ControlState)522 void SetValue(ControlState) override {}
523
CountNumControls() const524 int CountNumControls() const override
525 {
526 int result = 0;
527 for (auto& input : m_modifiers)
528 result += input->CountNumControls();
529 return result + m_final_input->CountNumControls();
530 }
531
UpdateReferences(ControlEnvironment & env)532 void UpdateReferences(ControlEnvironment& env) override
533 {
534 for (auto& input : m_modifiers)
535 input->UpdateReferences(env);
536
537 m_final_input->UpdateReferences(env);
538
539 // We must update our suppression with valid pointers.
540 if (m_suppressor)
541 EnableSuppression();
542 }
543
544 private:
EnableSuppression() const545 void EnableSuppression() const
546 {
547 if (!m_suppressor)
548 m_suppressor = s_hotkey_suppressions.MakeSuppressor(&m_modifiers, &m_final_input);
549 }
550
551 HotkeySuppressions::Modifiers m_modifiers;
552 std::unique_ptr<ControlExpression> m_final_input;
553 mutable HotkeySuppressions::Suppressor m_suppressor;
554 mutable bool m_is_blocked = false;
555 };
556
557 // This class proxies all methods to its either left-hand child if it has bound controls, or its
558 // right-hand child. Its intended use is for supporting old-style barewords expressions.
559 class CoalesceExpression : public Expression
560 {
561 public:
CoalesceExpression(std::unique_ptr<Expression> && lhs,std::unique_ptr<Expression> && rhs)562 CoalesceExpression(std::unique_ptr<Expression>&& lhs, std::unique_ptr<Expression>&& rhs)
563 : m_lhs(std::move(lhs)), m_rhs(std::move(rhs))
564 {
565 }
566
GetValue() const567 ControlState GetValue() const override { return GetActiveChild()->GetValue(); }
SetValue(ControlState value)568 void SetValue(ControlState value) override { GetActiveChild()->SetValue(value); }
569
CountNumControls() const570 int CountNumControls() const override { return GetActiveChild()->CountNumControls(); }
UpdateReferences(ControlEnvironment & env)571 void UpdateReferences(ControlEnvironment& env) override
572 {
573 m_lhs->UpdateReferences(env);
574 m_rhs->UpdateReferences(env);
575 }
576
577 private:
GetActiveChild() const578 const std::unique_ptr<Expression>& GetActiveChild() const
579 {
580 return m_lhs->CountNumControls() > 0 ? m_lhs : m_rhs;
581 }
582
583 std::unique_ptr<Expression> m_lhs;
584 std::unique_ptr<Expression> m_rhs;
585 };
586
FindDevice(ControlQualifier qualifier) const587 std::shared_ptr<Device> ControlEnvironment::FindDevice(ControlQualifier qualifier) const
588 {
589 if (qualifier.has_device)
590 return container.FindDevice(qualifier.device_qualifier);
591 else
592 return container.FindDevice(default_device);
593 }
594
FindInput(ControlQualifier qualifier) const595 Device::Input* ControlEnvironment::FindInput(ControlQualifier qualifier) const
596 {
597 const std::shared_ptr<Device> device = FindDevice(qualifier);
598 if (!device)
599 return nullptr;
600
601 return device->FindInput(qualifier.control_name);
602 }
603
FindOutput(ControlQualifier qualifier) const604 Device::Output* ControlEnvironment::FindOutput(ControlQualifier qualifier) const
605 {
606 const std::shared_ptr<Device> device = FindDevice(qualifier);
607 if (!device)
608 return nullptr;
609
610 return device->FindOutput(qualifier.control_name);
611 }
612
GetVariablePtr(const std::string & name)613 ControlState* ControlEnvironment::GetVariablePtr(const std::string& name)
614 {
615 return &m_variables[name];
616 }
617
MakeEmptyResult()618 ParseResult ParseResult::MakeEmptyResult()
619 {
620 ParseResult result;
621 result.status = ParseStatus::EmptyExpression;
622 return result;
623 }
624
MakeSuccessfulResult(std::unique_ptr<Expression> && expr)625 ParseResult ParseResult::MakeSuccessfulResult(std::unique_ptr<Expression>&& expr)
626 {
627 ParseResult result;
628 result.status = ParseStatus::Successful;
629 result.expr = std::move(expr);
630 return result;
631 }
632
MakeErrorResult(Token token,std::string description)633 ParseResult ParseResult::MakeErrorResult(Token token, std::string description)
634 {
635 ParseResult result;
636 result.status = ParseStatus::SyntaxError;
637 result.token = std::move(token);
638 result.description = std::move(description);
639 return result;
640 }
641
642 class Parser
643 {
644 public:
Parser(const std::vector<Token> & tokens_)645 explicit Parser(const std::vector<Token>& tokens_) : tokens(tokens_) { m_it = tokens.begin(); }
Parse()646 ParseResult Parse()
647 {
648 ParseResult result = ParseToplevel();
649
650 if (ParseStatus::Successful != result.status)
651 return result;
652
653 if (Peek().type == TOK_EOF)
654 return result;
655
656 return ParseResult::MakeErrorResult(Peek(), _trans("Expected end of expression."));
657 }
658
659 private:
660 const std::vector<Token>& tokens;
661 std::vector<Token>::const_iterator m_it;
662
Chew()663 Token Chew()
664 {
665 const Token tok = Peek();
666 if (TOK_EOF != tok.type)
667 ++m_it;
668 return tok;
669 }
670
Peek()671 Token Peek() { return *m_it; }
672
Expects(TokenType type)673 bool Expects(TokenType type)
674 {
675 Token tok = Chew();
676 return tok.type == type;
677 }
678
ParseFunctionArguments(const std::string_view & func_name,std::unique_ptr<FunctionExpression> && func,const Token & func_tok)679 ParseResult ParseFunctionArguments(const std::string_view& func_name,
680 std::unique_ptr<FunctionExpression>&& func,
681 const Token& func_tok)
682 {
683 std::vector<std::unique_ptr<Expression>> args;
684
685 if (TOK_LPAREN != Peek().type)
686 {
687 // Single argument with no parens (useful for unary ! function)
688 const auto tok = Chew();
689 auto arg = ParseAtom(tok);
690 if (ParseStatus::Successful != arg.status)
691 return arg;
692
693 args.emplace_back(std::move(arg.expr));
694 }
695 else
696 {
697 // Chew the L-Paren
698 Chew();
699
700 // Check for empty argument list:
701 if (TOK_RPAREN == Peek().type)
702 {
703 Chew();
704 }
705 else
706 {
707 while (true)
708 {
709 // Read one argument.
710 // Grab an expression, but stop at comma.
711 auto arg = ParseBinary(BinaryOperatorPrecedence(TOK_COMMA));
712 if (ParseStatus::Successful != arg.status)
713 return arg;
714
715 args.emplace_back(std::move(arg.expr));
716
717 // Right paren is the end of our arguments.
718 const Token tok = Chew();
719 if (TOK_RPAREN == tok.type)
720 break;
721
722 // Comma before the next argument.
723 if (TOK_COMMA != tok.type)
724 return ParseResult::MakeErrorResult(tok, _trans("Expected comma."));
725 };
726 }
727 }
728
729 const auto argument_validation = func->SetArguments(std::move(args));
730
731 if (std::holds_alternative<FunctionExpression::ExpectedArguments>(argument_validation))
732 {
733 const auto text = std::string(func_name) + '(' +
734 std::get<FunctionExpression::ExpectedArguments>(argument_validation).text +
735 ')';
736
737 return ParseResult::MakeErrorResult(func_tok, _trans("Expected arguments: " + text));
738 }
739
740 return ParseResult::MakeSuccessfulResult(std::move(func));
741 }
742
ParseAtom(const Token & tok)743 ParseResult ParseAtom(const Token& tok)
744 {
745 switch (tok.type)
746 {
747 case TOK_BAREWORD:
748 {
749 auto func = MakeFunctionExpression(tok.data);
750
751 if (!func)
752 {
753 // Invalid function, interpret this as a bareword control.
754 Token control_tok(tok);
755 control_tok.type = TOK_CONTROL;
756 return ParseAtom(control_tok);
757 }
758
759 return ParseFunctionArguments(tok.data, std::move(func), tok);
760 }
761 case TOK_CONTROL:
762 {
763 ControlQualifier cq;
764 cq.FromString(tok.data);
765 return ParseResult::MakeSuccessfulResult(std::make_unique<ControlExpression>(cq));
766 }
767 case TOK_NOT:
768 {
769 return ParseFunctionArguments("not", MakeFunctionExpression("not"), tok);
770 }
771 case TOK_LITERAL:
772 {
773 return MakeLiteralExpression(tok);
774 }
775 case TOK_VARIABLE:
776 {
777 return ParseResult::MakeSuccessfulResult(std::make_unique<VariableExpression>(tok.data));
778 }
779 case TOK_LPAREN:
780 {
781 return ParseParens();
782 }
783 case TOK_HOTKEY:
784 {
785 return ParseHotkeys();
786 }
787 case TOK_SUB:
788 {
789 // An atom was expected but we got a subtraction symbol.
790 // Interpret it as a unary minus function.
791 return ParseFunctionArguments("minus", MakeFunctionExpression("minus"), tok);
792 }
793 default:
794 {
795 return ParseResult::MakeErrorResult(tok, _trans("Expected start of expression."));
796 }
797 }
798 }
799
BinaryOperatorPrecedence(TokenType type)800 static int BinaryOperatorPrecedence(TokenType type)
801 {
802 switch (type)
803 {
804 case TOK_MUL:
805 case TOK_DIV:
806 case TOK_MOD:
807 return 1;
808 case TOK_ADD:
809 case TOK_SUB:
810 return 2;
811 case TOK_GTHAN:
812 case TOK_LTHAN:
813 return 3;
814 case TOK_AND:
815 return 4;
816 case TOK_XOR:
817 return 5;
818 case TOK_OR:
819 return 6;
820 case TOK_ASSIGN:
821 return 7;
822 case TOK_COMMA:
823 return 8;
824 default:
825 assert(false);
826 return 0;
827 }
828 }
829
ParseBinary(int precedence=999)830 ParseResult ParseBinary(int precedence = 999)
831 {
832 ParseResult lhs = ParseAtom(Chew());
833
834 if (lhs.status == ParseStatus::SyntaxError)
835 return lhs;
836
837 std::unique_ptr<Expression> expr = std::move(lhs.expr);
838
839 // TODO: handle LTR/RTL associativity?
840 while (Peek().IsBinaryOperator() && BinaryOperatorPrecedence(Peek().type) < precedence)
841 {
842 const Token tok = Chew();
843 ParseResult rhs = ParseBinary(BinaryOperatorPrecedence(tok.type));
844 if (rhs.status == ParseStatus::SyntaxError)
845 {
846 return rhs;
847 }
848
849 expr = std::make_unique<BinaryExpression>(tok.type, std::move(expr), std::move(rhs.expr));
850 }
851
852 return ParseResult::MakeSuccessfulResult(std::move(expr));
853 }
854
ParseParens()855 ParseResult ParseParens()
856 {
857 // lparen already chewed
858 ParseResult result = ParseToplevel();
859 if (result.status != ParseStatus::Successful)
860 return result;
861
862 const auto rparen = Chew();
863 if (rparen.type != TOK_RPAREN)
864 {
865 return ParseResult::MakeErrorResult(rparen, _trans("Expected closing paren."));
866 }
867
868 return result;
869 }
870
ParseHotkeys()871 ParseResult ParseHotkeys()
872 {
873 Token tok = Chew();
874 if (tok.type != TOK_LPAREN)
875 return ParseResult::MakeErrorResult(tok, _trans("Expected opening paren."));
876
877 std::vector<std::unique_ptr<ControlExpression>> inputs;
878
879 while (true)
880 {
881 tok = Chew();
882
883 if (tok.type != TOK_CONTROL && tok.type != TOK_BAREWORD)
884 return ParseResult::MakeErrorResult(tok, _trans("Expected name of input."));
885
886 ControlQualifier cq;
887 cq.FromString(tok.data);
888 inputs.emplace_back(std::make_unique<ControlExpression>(std::move(cq)));
889
890 tok = Chew();
891
892 if (tok.type == TOK_ADD)
893 continue;
894
895 if (tok.type == TOK_RPAREN)
896 break;
897
898 return ParseResult::MakeErrorResult(tok, _trans("Expected + or closing paren."));
899 }
900
901 return ParseResult::MakeSuccessfulResult(std::make_unique<HotkeyExpression>(std::move(inputs)));
902 }
903
ParseToplevel()904 ParseResult ParseToplevel() { return ParseBinary(); }
905 }; // namespace ExpressionParser
906
ParseTokens(const std::vector<Token> & tokens)907 ParseResult ParseTokens(const std::vector<Token>& tokens)
908 {
909 return Parser(tokens).Parse();
910 }
911
ParseComplexExpression(const std::string & str)912 static ParseResult ParseComplexExpression(const std::string& str)
913 {
914 Lexer l(str);
915 std::vector<Token> tokens;
916 const ParseStatus tokenize_status = l.Tokenize(tokens);
917 if (tokenize_status != ParseStatus::Successful)
918 return ParseResult::MakeErrorResult(Token(TOK_INVALID), _trans("Tokenizing failed."));
919
920 RemoveInertTokens(&tokens);
921 return ParseTokens(tokens);
922 }
923
RemoveInertTokens(std::vector<Token> * tokens)924 void RemoveInertTokens(std::vector<Token>* tokens)
925 {
926 tokens->erase(std::remove_if(tokens->begin(), tokens->end(),
927 [](const Token& tok) {
928 return tok.type == TOK_COMMENT || tok.type == TOK_WHITESPACE;
929 }),
930 tokens->end());
931 }
932
ParseBarewordExpression(const std::string & str)933 static std::unique_ptr<Expression> ParseBarewordExpression(const std::string& str)
934 {
935 ControlQualifier qualifier;
936 qualifier.control_name = str;
937 qualifier.has_device = false;
938
939 return std::make_unique<ControlExpression>(qualifier);
940 }
941
ParseExpression(const std::string & str)942 ParseResult ParseExpression(const std::string& str)
943 {
944 if (StripSpaces(str).empty())
945 return ParseResult::MakeEmptyResult();
946
947 auto bareword_expr = ParseBarewordExpression(str);
948 ParseResult complex_result = ParseComplexExpression(str);
949
950 if (complex_result.status != ParseStatus::Successful)
951 {
952 // This is a bit odd.
953 // Return the error status of the complex expression with the fallback barewords expression.
954 complex_result.expr = std::move(bareword_expr);
955 return complex_result;
956 }
957
958 complex_result.expr = std::make_unique<CoalesceExpression>(std::move(bareword_expr),
959 std::move(complex_result.expr));
960 return complex_result;
961 }
962 } // namespace ciface::ExpressionParser
963