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