]> git.sesse.net Git - casparcg/blob - core/producer/scene/expression_parser.cpp
Merge pull request #545 from aruanoc/2.1.0
[casparcg] / core / producer / scene / expression_parser.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Helge Norberg, helge.norberg@svt.se
20 */
21
22 #include "../../StdAfx.h"
23
24 #include "expression_parser.h"
25
26 #include <string>
27 #include <memory>
28 #include <vector>
29 #include <functional>
30 #include <typeinfo>
31 #include <cstdint>
32 #include <cmath>
33
34 #include <boost/any.hpp>
35
36 #include <common/log.h>
37 #include <common/except.h>
38 #include <common/utf.h>
39 #include <common/tweener.h>
40
41 namespace caspar { namespace core { namespace scene {
42
43 wchar_t next_non_whitespace(
44                 std::wstring::const_iterator& cursor,
45                 const std::wstring& str,
46                 const std::wstring& error_if_eof)
47 {
48         while (cursor != str.end())
49         {
50                 switch (*cursor)
51                 {
52                 case L' ':
53                 case L'\t':
54                         ++cursor;
55                         continue;
56                 default:
57                         return *cursor;
58                 }
59         }
60
61         CASPAR_THROW_EXCEPTION(user_error() << msg_info(
62                         L"Unexpected end of input (" + error_if_eof + L") in " + str));
63 }
64
65 std::wstring at_position(
66                 const std::wstring::const_iterator& cursor, const std::wstring& str)
67 {
68         int index = static_cast<int>(cursor - str.begin());
69
70         return L" at index " + boost::lexical_cast<std::wstring>(index)
71                         + L" in " + str;
72 }
73
74 boost::any as_binding(const boost::any& value);
75
76 template<typename T>
77 binding<T> require(const boost::any& value)
78 {
79         auto b = as_binding(value);
80
81         if (is<binding<T>>(b))
82                 return as<binding<T>>(b);
83         else
84                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
85                         L"Required binding of type " + u16(typeid(T).name())
86                         + L" but got " + u16(value.type().name())));
87 }
88
89 boost::any parse_expression(
90                 std::wstring::const_iterator& cursor,
91                 const std::wstring& str,
92                 const variable_repository& var_repo);
93
94 boost::any parse_parenthesis(
95                 std::wstring::const_iterator& cursor,
96                 const std::wstring& str,
97                 const variable_repository& var_repo)
98 {
99         if (*cursor++ != L'(')
100                 CASPAR_THROW_EXCEPTION(user_error()
101                                 << msg_info(L"Expected (" + at_position(cursor, str)));
102
103         auto expr = parse_expression(cursor, str, var_repo);
104
105         if (next_non_whitespace(cursor, str, L"Expected )") != L')')
106                 CASPAR_THROW_EXCEPTION(user_error()
107                                 << msg_info(L"Expected )" + at_position(cursor, str)));
108
109         ++cursor;
110
111         return expr;
112 }
113
114 boost::any create_animate_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
115 {
116         if (params.size() != 3)
117                 CASPAR_THROW_EXCEPTION(user_error()
118                         << msg_info(L"animate() function requires three parameters: to_animate, duration, tweener"));
119
120         auto to_animate         = require<double>(params.at(0));
121         auto frame_counter      = var_repo(L"frame").as<double>();
122         auto duration           = require<double>(params.at(1));
123         auto tw                         = require<std::wstring>(params.at(2)).transformed([](const std::wstring& s) { return tweener(s); });
124
125         return to_animate.animated(frame_counter, duration, tw);
126 }
127
128 boost::any create_sin_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
129 {
130         if (params.size() != 1)
131                 CASPAR_THROW_EXCEPTION(user_error()
132                         << msg_info(L"sin() function requires one parameters: angle"));
133
134         auto angle = require<double>(params.at(0));
135
136         return angle.transformed([](double a) { return std::sin(a); });
137 }
138
139 boost::any create_cos_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
140 {
141         if (params.size() != 1)
142                 CASPAR_THROW_EXCEPTION(user_error()
143                         << msg_info(L"cos() function requires one parameters: angle"));
144
145         auto angle = require<double>(params.at(0));
146
147         return angle.transformed([](double a) { return std::cos(a); });
148 }
149
150 boost::any create_abs_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
151 {
152         if (params.size() != 1)
153                 CASPAR_THROW_EXCEPTION(user_error()
154                         << msg_info(L"abs() function requires one parameters: value"));
155
156         auto val = require<double>(params.at(0));
157
158         return val.transformed([](double v) { return std::abs(v); });
159 }
160
161 boost::any parse_function(
162                 const std::wstring& function_name,
163                 std::wstring::const_iterator& cursor,
164                 const std::wstring& str,
165                 const variable_repository& var_repo)
166 {
167         static std::map<std::wstring, std::function<boost::any (const std::vector<boost::any>& params, const variable_repository& var_repo)>> FUNCTIONS
168         {
169                 {L"animate",    create_animate_function },
170                 {L"sin",                create_sin_function },
171                 {L"cos",                create_cos_function },
172                 {L"abs",                create_abs_function }
173         };
174
175         auto function = FUNCTIONS.find(function_name);
176
177         if (function == FUNCTIONS.end())
178                 CASPAR_THROW_EXCEPTION(user_error()
179                                 << msg_info(function_name + L"() is an unknown function" + at_position(cursor, str)));
180
181         if (*cursor++ != L'(')
182                 CASPAR_THROW_EXCEPTION(user_error()
183                         << msg_info(L"Expected (" + at_position(cursor, str)));
184
185         std::vector<boost::any> params;
186
187         while (cursor != str.end())
188         {
189                 params.push_back(parse_expression(cursor, str, var_repo));
190
191                 auto next = next_non_whitespace(cursor, str, L"Expected , or )");
192
193                 if (next == L')')
194                         break;
195                 else if (next != L',')
196                         CASPAR_THROW_EXCEPTION(user_error()
197                                 << msg_info(L"Expected ) or ," + at_position(cursor, str)));
198
199                 ++cursor;
200         }
201
202         if (next_non_whitespace(cursor, str, L"Expected , or )") != L')')
203                 CASPAR_THROW_EXCEPTION(user_error()
204                         << msg_info(L"Expected ) " + at_position(cursor, str)));
205
206         ++cursor;
207
208         return function->second(params, var_repo);
209 }
210
211 double parse_constant(
212                 std::wstring::const_iterator& cursor, const std::wstring& str)
213 {
214         std::wstring constant;
215
216         while (cursor != str.end())
217         {
218                 wchar_t ch = *cursor;
219
220                 if ((ch >= L'0' && ch <= L'9') || ch == L'.')
221                         constant += ch;
222                 else
223                         break;
224
225                 ++cursor;
226         }
227
228         return boost::lexical_cast<double>(constant);
229 }
230
231 std::wstring parse_string_literal(
232                 std::wstring::const_iterator& cursor, const std::wstring& str)
233 {
234         std::wstring literal;
235
236         if (*cursor++ != L'"')
237                 CASPAR_THROW_EXCEPTION(user_error()
238                                 << msg_info(L"Expected (" + at_position(cursor, str)));
239
240         bool escaping = false;
241
242         while (cursor != str.end())
243         {
244                 wchar_t ch = *cursor;
245
246                 switch (ch)
247                 {
248                 case L'\\':
249                         if (escaping)
250                         {
251                                 literal += ch;
252                                 escaping = false;
253                         }
254                         else
255                                 escaping = true;
256                         break;
257                 case L'"':
258                         if (escaping)
259                         {
260                                 literal += ch;
261                                 escaping = false;
262                                 break;
263                         }
264                         else
265                         {
266                                 ++cursor;
267                                 return std::move(literal);
268                         }
269                 case L'n':
270                         if (escaping)
271                         {
272                                 literal += L'\n';
273                                 escaping = false;
274                         }
275                         else
276                                 literal += ch;
277                         break;
278                 default:
279                         literal += ch;
280                 }
281
282                 ++cursor;
283         }
284
285         CASPAR_THROW_EXCEPTION(user_error() << msg_info(
286                         L"Unexpected end of input (Expected closing \") in " + str));
287 }
288
289 boost::any parse_variable(
290                 std::wstring::const_iterator& cursor,
291                 const std::wstring& str,
292                 const variable_repository& var_repo)
293 {
294         std::wstring variable_name;
295
296         while (cursor != str.end())
297         {
298                 wchar_t ch = *cursor;
299
300                 if (ch == L'.'
301                                 || ch == L'_'
302                                 || (ch >= L'a' && ch <= L'z')
303                                 || (ch >= L'A' && ch <= L'Z')
304                                 || (variable_name.length() > 0 && ch >= L'0' && ch <= L'9'))
305                         variable_name += ch;
306                 else
307                         break;
308
309                 ++cursor;
310         }
311
312         if (cursor != str.end() && *cursor == L'(')
313                 return variable_name;
314
315         if (variable_name == L"true")
316                 return true;
317         else if (variable_name == L"false")
318                 return false;
319
320         variable& var = var_repo(variable_name);
321
322         if (var.is<double>())
323                 return var.as<double>();
324         else if (var.is<int64_t>())
325                 return var.as<int64_t>().as<double>();
326         else if (var.is<std::wstring>())
327                 return var.as<std::wstring>();
328         else if (var.is<bool>())
329                 return var.as<bool>();
330
331         CASPAR_THROW_EXCEPTION(user_error() << msg_info(
332                                 L"Unhandled variable type of " + variable_name
333                                 + at_position(cursor, str)));
334 }
335
336 struct op
337 {
338         enum class op_type
339         {
340                 UNARY,
341                 BINARY,
342                 TERNARY
343         };
344
345         std::wstring characters;
346         int precedence;
347         op_type type;
348
349         op(wchar_t ch, int precedence, op_type type)
350                 : characters(1, ch), precedence(precedence), type(type)
351         {
352         }
353
354         op(const std::wstring& chs, int precedence, op_type type)
355                 : characters(chs), precedence(precedence), type(type)
356         {
357         }
358 };
359
360 op parse_operator(std::wstring::const_iterator& cursor, const std::wstring& str)
361 {
362         static const wchar_t NONE = L' ';
363         wchar_t first = NONE;
364
365         while (cursor != str.end())
366         {
367                 wchar_t ch = *cursor;
368
369                 switch (ch)
370                 {
371                 case L'+':
372                         ++cursor;
373                         return op(ch, 6, op::op_type::BINARY);
374                 case L'*':
375                 case L'/':
376                 case L'%':
377                         ++cursor;
378                         return op(ch, 5, op::op_type::BINARY);
379                 case L'?':
380                 case L':':
381                         ++cursor;
382                         return op(ch, 15, op::op_type::TERNARY);
383                 case L'-':
384                         if (first == L'-')
385                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
386                                                 L"Did not expect -" + at_position(cursor, str)));
387                         else
388                                 first = ch;
389
390                         ++cursor;
391                         break;
392                 case L'!':
393                         if (first == L'!')
394                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
395                                                 L"Did not expect !" + at_position(cursor, str)));
396                         else
397                                 first = ch;
398
399                         ++cursor;
400                         break;
401                 case L'<':
402                         if (first == L'<')
403                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
404                                                 L"Did not expect <" + at_position(cursor, str)));
405                         else
406                                 first = ch;
407
408                         ++cursor;
409                         break;
410                 case L'>':
411                         if (first == L'>')
412                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
413                                                 L"Did not expect >" + at_position(cursor, str)));
414                         else
415                                 first = ch;
416
417                         ++cursor;
418                         break;
419                 case L'=':
420                         if (first == L'=')
421                         {
422                                 ++cursor;
423                                 return op(L"==", 9, op::op_type::BINARY);
424                         }
425                         else if (first == L'!')
426                         {
427                                 ++cursor;
428                                 return op(L"!=", 9, op::op_type::BINARY);
429                         }
430                         else if (first == L'>')
431                         {
432                                 ++cursor;
433                                 return op(L">=", 8, op::op_type::BINARY);
434                         }
435                         else if (first == L'<')
436                         {
437                                 ++cursor;
438                                 return op(L"<=", 8, op::op_type::BINARY);
439                         }
440                         else if (first == NONE)
441                         {
442                                 ++cursor;
443                                 first = L'=';
444                         }
445                         else
446                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
447                                                 L"Did not expect =" + at_position(cursor, str)));
448
449                         break;
450                 case L'|':
451                         if (first == L'|')
452                         {
453                                 ++cursor;
454                                 return op(L"||", 14, op::op_type::BINARY);
455                         }
456                         else if (first == NONE)
457                         {
458                                 ++cursor;
459                                 first = L'|';
460                         }
461                         else
462                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
463                                                 L"Did not expect =" + at_position(cursor, str)));
464
465                         break;
466                 case L'&':
467                         if (first == L'&')
468                         {
469                                 ++cursor;
470                                 return op(L"&&", 13, op::op_type::BINARY);
471                         }
472                         else if (first == NONE)
473                         {
474                                 ++cursor;
475                                 first = L'&';
476                         }
477                         else
478                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
479                                                 L"Did not expect =" + at_position(cursor, str)));
480
481                         break;
482                 case L' ':
483                 case L'\t':
484                         if (first == L'-')
485                                 return op(L'-', 6, op::op_type::BINARY);
486                         else if (first == L'!')
487                                 return op(L'!', 3, op::op_type::UNARY);
488                 default:
489                         if (first == L'<')
490                                 return op(L'<', 8, op::op_type::BINARY);
491                         else if (first == L'>')
492                                 return op(L'>', 8, op::op_type::BINARY);
493                         else if (first == L'-')
494                                 return op(L"unary-", 3, op::op_type::UNARY);
495                         else if (first == L'!')
496                                 return op(L'!', 3, op::op_type::UNARY);
497                         else
498                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
499                                                 L"Expected second character of operator"
500                                                 + at_position(cursor, str)));
501                 }
502         }
503
504         CASPAR_THROW_EXCEPTION(user_error() << msg_info(
505                         L"Unexpected end of input (Expected operator) in " + str));
506 }
507
508 boost::any as_binding(const boost::any& value)
509 {
510         // Wrap supported constants as bindings
511         if (is<double>(value))
512                 return binding<double>(as<double>(value));
513         else if (is<bool>(value))
514                 return binding<bool>(as<bool>(value));
515         else if (is<std::wstring>(value))
516                 return binding<std::wstring>(as<std::wstring>(value));
517         // Already one of the supported binding types
518         else if (is<binding<double>>(value))
519                 return as<binding<double>>(value);
520         else if (is<binding<bool>>(value))
521                 return as<binding<bool>>(value);
522         else if (is<binding<std::wstring>>(value))
523                 return as<binding<std::wstring>>(value);
524         else
525                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
526                                 L"Couldn't detect type of " + u16(value.type().name())));
527 }
528
529 boost::any negative(const boost::any& to_create_negative_of)
530 {
531         return -require<double>(to_create_negative_of);
532 }
533
534 boost::any not_(const boost::any& to_create_not_of)
535 {
536         return !require<bool>(to_create_not_of);
537 }
538
539 boost::any multiply(const boost::any& lhs, boost::any& rhs)
540 {
541         return require<double>(lhs) * require<double>(rhs);
542 }
543
544 boost::any divide(const boost::any& lhs, boost::any& rhs)
545 {
546         return require<double>(lhs) / require<double>(rhs);
547 }
548
549 boost::any modulus(const boost::any& lhs, boost::any& rhs)
550 {
551         return
552                         (
553                                         require<double>(lhs).as<int64_t>()
554                                         % require<double>(rhs).as<int64_t>()
555                         ).as<double>();
556 }
557
558 binding<std::wstring> stringify(const boost::any& value)
559 {
560         auto b = as_binding(value);
561
562         if (is<binding<std::wstring>>(b))
563                 return as<binding<std::wstring>>(b);
564         else if (is<binding<double>>(b))
565                 return as<binding<double>>(b).as<std::wstring>();
566         else if (is<binding<bool>>(b))
567                 return as<binding<bool>>(b).as<std::wstring>();
568         else
569                 CASPAR_THROW_EXCEPTION(user_error()
570                                 << msg_info(L"Couldn't stringify " + u16(value.type().name())));
571 }
572
573 boost::any add(const boost::any& lhs, boost::any& rhs)
574 {
575         auto l = as_binding(lhs);
576         auto r = as_binding(rhs);
577
578         // number
579         if (is<binding<double>>(l) && is<binding<double>>(r))
580                 return as<binding<double>>(l) + as<binding<double>>(r);
581         // string
582         else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
583                 return as<binding<std::wstring>>(l) + as<binding<std::wstring>>(r);
584         // mixed types to string and concatenated
585         else
586                 return stringify(lhs) + stringify(rhs);
587 }
588
589 boost::any subtract(const boost::any& lhs, boost::any& rhs)
590 {
591         return require<double>(lhs) - require<double>(rhs);
592 }
593
594 boost::any less(const boost::any& lhs, boost::any& rhs)
595 {
596         return require<double>(lhs) < require<double>(rhs);
597 }
598
599 boost::any less_or_equal(const boost::any& lhs, boost::any& rhs)
600 {
601         return require<double>(lhs) <= require<double>(rhs);
602 }
603
604 boost::any greater(const boost::any& lhs, boost::any& rhs)
605 {
606         return require<double>(lhs) > require<double>(rhs);
607 }
608
609 boost::any greater_or_equal(const boost::any& lhs, boost::any& rhs)
610 {
611         return require<double>(lhs) >= require<double>(rhs);
612 }
613
614 boost::any equal(const boost::any& lhs, boost::any& rhs)
615 {
616         auto l = as_binding(lhs);
617         auto r = as_binding(rhs);
618
619         // number
620         if (is<binding<double>>(l) && is<binding<double>>(r))
621                 return as<binding<double>>(l) == as<binding<double>>(r);
622         // string
623         else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
624                 return as<binding<std::wstring>>(l) == as<binding<std::wstring>>(r);
625         // boolean
626         else
627                 return require<bool>(l) == require<bool>(r);
628 }
629
630 boost::any and_(const boost::any& lhs, boost::any& rhs)
631 {
632         return require<bool>(lhs) && require<bool>(rhs);
633 }
634
635 boost::any or_(const boost::any& lhs, boost::any& rhs)
636 {
637         return require<bool>(lhs) || require<bool>(rhs);
638 }
639
640 template<typename T>
641 binding<T> ternary(
642                 const binding<bool>& condition,
643                 const binding<T>& true_value,
644                 const binding<T>& false_value)
645 {
646         return when(condition).then(true_value).otherwise(false_value);
647 }
648
649 boost::any ternary(
650                 const boost::any& condition,
651                 const boost::any& true_value,
652                 const boost::any& false_value)
653 {
654         auto cond = require<bool>(condition);
655         auto t = as_binding(true_value);
656         auto f = as_binding(false_value);
657
658         // double
659         if (is<binding<double>>(t) && is<binding<double>>(f))
660                 return ternary(cond, as<binding<double>>(t), as<binding<double>>(f));
661         // string
662         else if (is<binding<std::wstring>>(t) && is<binding<std::wstring>>(f))
663                 return ternary(
664                                 cond,
665                                 as<binding<std::wstring>>(t),
666                                 as<binding<std::wstring>>(f));
667         // bool
668         else
669                 return ternary(cond, require<bool>(t), require<bool>(f));
670 }
671
672 void resolve_operators(int precedence, std::vector<boost::any>& tokens)
673 {
674         for (int i = 0; i < tokens.size(); ++i)
675         {
676                 auto& token = tokens.at(i);
677
678                 if (!is<op>(token))
679                         continue;
680
681                 auto op_token = as<op>(token);
682
683                 if (op_token.precedence != precedence)
684                         continue;
685
686                 int index_after = i + 1;
687                 auto& token_after = tokens.at(index_after);
688
689                 switch (op_token.type)
690                 {
691                 case op::op_type::UNARY:
692                         if (op_token.characters == L"unary-")
693                         {
694                                 tokens.at(i) = negative(token_after);
695                         }
696                         else if (op_token.characters == L"!")
697                         {
698                                 tokens.at(i) = not_(token_after);
699                         }
700
701                         tokens.erase(tokens.begin() + index_after);
702
703                         break;
704                 case op::op_type::BINARY:
705                         {
706                                 auto& token_before = tokens.at(i - 1);
707
708                                 if (op_token.characters == L"*")
709                                         token_before = multiply(token_before, token_after);
710                                 else if (op_token.characters == L"/")
711                                         token_before = divide(token_before, token_after);
712                                 else if (op_token.characters == L"%")
713                                         token_before = modulus(token_before, token_after);
714                                 else if (op_token.characters == L"+")
715                                         token_before = add(token_before, token_after);
716                                 else if (op_token.characters == L"-")
717                                         token_before = subtract(token_before, token_after);
718                                 else if (op_token.characters == L"<")
719                                         token_before = less(token_before, token_after);
720                                 else if (op_token.characters == L"<=")
721                                         token_before = less_or_equal(token_before, token_after);
722                                 else if (op_token.characters == L">")
723                                         token_before = greater(token_before, token_after);
724                                 else if (op_token.characters == L">=")
725                                         token_before = greater_or_equal(token_before, token_after);
726                                 else if (op_token.characters == L"==")
727                                         token_before = equal(token_before, token_after);
728                                 else if (op_token.characters == L"!=")
729                                         token_before = not_(equal(token_before, token_after));
730                                 else if (op_token.characters == L"&&")
731                                         token_before = and_(token_before, token_after);
732                                 else if (op_token.characters == L"||")
733                                         token_before = or_(token_before, token_after);
734                         }
735
736                         tokens.erase(tokens.begin() + i, tokens.begin() + i + 2);
737                         --i;
738
739                         break;
740                 case op::op_type::TERNARY:
741                         if (op_token.characters == L"?")
742                         {
743                                 auto& token_before = tokens.at(i - 1);
744                                 auto& token_colon_operator = tokens.at(i + 2);
745
746                                 if (as<op>(token_colon_operator).characters != L":")
747                                         CASPAR_THROW_EXCEPTION(user_error() << msg_info(
748                                                         L"Expected : as part of ternary expression"));
749
750                                 auto& token_false_value = tokens.at(i + 3);
751                                 token_before = ternary(
752                                                 token_before, token_after, token_false_value);
753                                 tokens.erase(tokens.begin() + i, tokens.begin() + i + 4);
754                                 --i;
755                         }
756
757                         break;
758                 }
759         }
760 }
761
762 boost::any parse_expression(
763                 std::wstring::const_iterator& cursor,
764                 const std::wstring& str,
765                 const variable_repository& var_repo)
766 {
767         std::vector<boost::any> tokens;
768         bool stop = false;
769
770         while (cursor != str.end())
771         {
772                 wchar_t ch = next_non_whitespace(cursor, str, L"Expected expression");
773
774                 switch (ch)
775                 {
776                 case L'0':
777                 case L'1':
778                 case L'2':
779                 case L'3':
780                 case L'4':
781                 case L'5':
782                 case L'6':
783                 case L'7':
784                 case L'8':
785                 case L'9':
786                         tokens.push_back(parse_constant(cursor, str));
787                         break;
788                 case L'+':
789                 case L'-':
790                 case L'*':
791                 case L'/':
792                 case L'%':
793                 case L'<':
794                 case L'>':
795                 case L'!':
796                 case L'=':
797                 case L'|':
798                 case L'&':
799                 case L'?':
800                 case L':':
801                         tokens.push_back(parse_operator(cursor, str));
802                         break;
803                 case L'"':
804                         tokens.push_back(parse_string_literal(cursor, str));
805                         break;
806                 case L'(':
807                         if (!tokens.empty() && is<std::wstring>(tokens.back()))
808                         {
809                                 auto function_name = as<std::wstring>(tokens.back());
810                                 tokens.pop_back();
811                                 tokens.push_back(parse_function(function_name, cursor, str, var_repo));
812                         }
813                         else
814                                 tokens.push_back(parse_parenthesis(cursor, str, var_repo));
815                         break;
816                 case L')':
817                 case L',':
818                         stop = true;
819                         break;
820                 default:
821                         tokens.push_back(parse_variable(cursor, str, var_repo));
822                         break;
823                 }
824
825                 if (stop)
826                         break;
827         }
828
829         if (tokens.empty())
830                 CASPAR_THROW_EXCEPTION(user_error()
831                                 << msg_info(L"Expected expression" + at_position(cursor, str)));
832
833         int precedence = 1;
834
835         while (tokens.size() > 1)
836         {
837                 resolve_operators(precedence++, tokens);
838         }
839
840         return as_binding(tokens.at(0));
841 }
842
843 }}}