]> git.sesse.net Git - casparcg/blob - core/producer/scene/expression_parser.cpp
Changed meaning of frame variable in scene to a continously animating variable. The...
[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 parse_function(
151                 const std::wstring& function_name,
152                 std::wstring::const_iterator& cursor,
153                 const std::wstring& str,
154                 const variable_repository& var_repo)
155 {
156         static std::map<std::wstring, std::function<boost::any (const std::vector<boost::any>& params, const variable_repository& var_repo)>> FUNCTIONS
157         {
158                 {L"animate",    create_animate_function },
159                 {L"sin",                create_sin_function },
160                 {L"cos",                create_cos_function }
161         };
162
163         auto function = FUNCTIONS.find(function_name);
164
165         if (function == FUNCTIONS.end())
166                 CASPAR_THROW_EXCEPTION(user_error()
167                                 << msg_info(function_name + L"() is an unknown function" + at_position(cursor, str)));
168
169         if (*cursor++ != L'(')
170                 CASPAR_THROW_EXCEPTION(user_error()
171                         << msg_info(L"Expected (" + at_position(cursor, str)));
172
173         std::vector<boost::any> params;
174
175         while (cursor != str.end())
176         {
177                 params.push_back(parse_expression(cursor, str, var_repo));
178
179                 auto next = next_non_whitespace(cursor, str, L"Expected , or )");
180
181                 if (next == L')')
182                         break;
183                 else if (next != L',')
184                         CASPAR_THROW_EXCEPTION(user_error()
185                                 << msg_info(L"Expected ) or ," + at_position(cursor, str)));
186
187                 ++cursor;
188         }
189
190         if (next_non_whitespace(cursor, str, L"Expected , or )") != L')')
191                 CASPAR_THROW_EXCEPTION(user_error()
192                         << msg_info(L"Expected ) " + at_position(cursor, str)));
193
194         ++cursor;
195
196         return function->second(params, var_repo);
197 }
198
199 double parse_constant(
200                 std::wstring::const_iterator& cursor, const std::wstring& str)
201 {
202         std::wstring constant;
203
204         while (cursor != str.end())
205         {
206                 wchar_t ch = *cursor;
207
208                 if ((ch >= L'0' && ch <= L'9') || ch == L'.')
209                         constant += ch;
210                 else
211                         break;
212
213                 ++cursor;
214         }
215
216         return boost::lexical_cast<double>(constant);
217 }
218
219 std::wstring parse_string_literal(
220                 std::wstring::const_iterator& cursor, const std::wstring& str)
221 {
222         std::wstring literal;
223
224         if (*cursor++ != L'"')
225                 CASPAR_THROW_EXCEPTION(user_error()
226                                 << msg_info(L"Expected (" + at_position(cursor, str)));
227
228         bool escaping = false;
229
230         while (cursor != str.end())
231         {
232                 wchar_t ch = *cursor;
233
234                 switch (ch)
235                 {
236                 case L'\\':
237                         if (escaping)
238                         {
239                                 literal += ch;
240                                 escaping = false;
241                         }
242                         else
243                                 escaping = true;
244                         break;
245                 case L'"':
246                         if (escaping)
247                         {
248                                 literal += ch;
249                                 escaping = false;
250                                 break;
251                         }
252                         else
253                         {
254                                 ++cursor;
255                                 return std::move(literal);
256                         }
257                 case L'n':
258                         if (escaping)
259                         {
260                                 literal += L'\n';
261                                 escaping = false;
262                         }
263                         else
264                                 literal += ch;
265                         break;
266                 default:
267                         literal += ch;
268                 }
269
270                 ++cursor;
271         }
272
273         CASPAR_THROW_EXCEPTION(user_error() << msg_info(
274                         L"Unexpected end of input (Expected closing \") in " + str));
275 }
276
277 boost::any parse_variable(
278                 std::wstring::const_iterator& cursor,
279                 const std::wstring& str,
280                 const variable_repository& var_repo)
281 {
282         std::wstring variable_name;
283
284         while (cursor != str.end())
285         {
286                 wchar_t ch = *cursor;
287
288                 if (ch == L'.'
289                                 || ch == L'_'
290                                 || (ch >= L'a' && ch <= L'z')
291                                 || (ch >= L'A' && ch <= L'Z')
292                                 || (variable_name.length() > 0 && ch >= L'0' && ch <= L'9'))
293                         variable_name += ch;
294                 else
295                         break;
296
297                 ++cursor;
298         }
299
300         if (cursor != str.end() && *cursor == L'(')
301                 return variable_name;
302
303         if (variable_name == L"true")
304                 return true;
305         else if (variable_name == L"false")
306                 return false;
307
308         variable& var = var_repo(variable_name);
309
310         if (var.is<double>())
311                 return var.as<double>();
312         else if (var.is<int64_t>())
313                 return var.as<int64_t>().as<double>();
314         else if (var.is<std::wstring>())
315                 return var.as<std::wstring>();
316         else if (var.is<bool>())
317                 return var.as<bool>();
318
319         CASPAR_THROW_EXCEPTION(user_error() << msg_info(
320                                 L"Unhandled variable type of " + variable_name
321                                 + at_position(cursor, str)));
322 }
323
324 struct op
325 {
326         enum class op_type
327         {
328                 UNARY,
329                 BINARY,
330                 TERNARY
331         };
332
333         std::wstring characters;
334         int precedence;
335         op_type type;
336
337         op(wchar_t ch, int precedence, op_type type)
338                 : characters(1, ch), precedence(precedence), type(type)
339         {
340         }
341
342         op(const std::wstring& chs, int precedence, op_type type)
343                 : characters(chs), precedence(precedence), type(type)
344         {
345         }
346 };
347
348 op parse_operator(std::wstring::const_iterator& cursor, const std::wstring& str)
349 {
350         static const wchar_t NONE = L' ';
351         wchar_t first = NONE;
352
353         while (cursor != str.end())
354         {
355                 wchar_t ch = *cursor;
356
357                 switch (ch)
358                 {
359                 case L'+':
360                         ++cursor;
361                         return op(ch, 6, op::op_type::BINARY);
362                 case L'*':
363                 case L'/':
364                 case L'%':
365                         ++cursor;
366                         return op(ch, 5, op::op_type::BINARY);
367                 case L'?':
368                 case L':':
369                         ++cursor;
370                         return op(ch, 15, op::op_type::TERNARY);
371                 case L'-':
372                         if (first == L'-')
373                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
374                                                 L"Did not expect -" + at_position(cursor, str)));
375                         else
376                                 first = ch;
377
378                         ++cursor;
379                         break;
380                 case L'!':
381                         if (first == L'!')
382                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
383                                                 L"Did not expect !" + at_position(cursor, str)));
384                         else
385                                 first = ch;
386
387                         ++cursor;
388                         break;
389                 case L'<':
390                         if (first == L'<')
391                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
392                                                 L"Did not expect <" + at_position(cursor, str)));
393                         else
394                                 first = ch;
395
396                         ++cursor;
397                         break;
398                 case L'>':
399                         if (first == L'>')
400                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
401                                                 L"Did not expect >" + at_position(cursor, str)));
402                         else
403                                 first = ch;
404
405                         ++cursor;
406                         break;
407                 case L'=':
408                         if (first == L'=')
409                         {
410                                 ++cursor;
411                                 return op(L"==", 9, op::op_type::BINARY);
412                         }
413                         else if (first == L'!')
414                         {
415                                 ++cursor;
416                                 return op(L"!=", 9, op::op_type::BINARY);
417                         }
418                         else if (first == L'>')
419                         {
420                                 ++cursor;
421                                 return op(L">=", 8, op::op_type::BINARY);
422                         }
423                         else if (first == L'<')
424                         {
425                                 ++cursor;
426                                 return op(L"<=", 8, op::op_type::BINARY);
427                         }
428                         else if (first == NONE)
429                         {
430                                 ++cursor;
431                                 first = L'=';
432                         }
433                         else
434                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
435                                                 L"Did not expect =" + at_position(cursor, str)));
436
437                         break;
438                 case L'|':
439                         if (first == L'|')
440                         {
441                                 ++cursor;
442                                 return op(L"||", 14, op::op_type::BINARY);
443                         }
444                         else if (first == NONE)
445                         {
446                                 ++cursor;
447                                 first = L'|';
448                         }
449                         else
450                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
451                                                 L"Did not expect =" + at_position(cursor, str)));
452
453                         break;
454                 case L'&':
455                         if (first == L'&')
456                         {
457                                 ++cursor;
458                                 return op(L"&&", 13, op::op_type::BINARY);
459                         }
460                         else if (first == NONE)
461                         {
462                                 ++cursor;
463                                 first = L'&';
464                         }
465                         else
466                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
467                                                 L"Did not expect =" + at_position(cursor, str)));
468
469                         break;
470                 case L' ':
471                 case L'\t':
472                         if (first == L'-')
473                                 return op(L'-', 6, op::op_type::BINARY);
474                         else if (first == L'!')
475                                 return op(L'!', 3, op::op_type::UNARY);
476                 default:
477                         if (first == L'<')
478                                 return op(L'<', 8, op::op_type::BINARY);
479                         else if (first == L'>')
480                                 return op(L'>', 8, op::op_type::BINARY);
481                         else if (first == L'-')
482                                 return op(L"unary-", 3, op::op_type::UNARY);
483                         else if (first == L'!')
484                                 return op(L'!', 3, op::op_type::UNARY);
485                         else
486                                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
487                                                 L"Expected second character of operator"
488                                                 + at_position(cursor, str)));
489                 }
490         }
491
492         CASPAR_THROW_EXCEPTION(user_error() << msg_info(
493                         L"Unexpected end of input (Expected operator) in " + str));
494 }
495
496 boost::any as_binding(const boost::any& value)
497 {
498         // Wrap supported constants as bindings
499         if (is<double>(value))
500                 return binding<double>(as<double>(value));
501         else if (is<bool>(value))
502                 return binding<bool>(as<bool>(value));
503         else if (is<std::wstring>(value))
504                 return binding<std::wstring>(as<std::wstring>(value));
505         // Already one of the supported binding types
506         else if (is<binding<double>>(value))
507                 return as<binding<double>>(value);
508         else if (is<binding<bool>>(value))
509                 return as<binding<bool>>(value);
510         else if (is<binding<std::wstring>>(value))
511                 return as<binding<std::wstring>>(value);
512         else
513                 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
514                                 L"Couldn't detect type of " + u16(value.type().name())));
515 }
516
517 boost::any negative(const boost::any& to_create_negative_of)
518 {
519         return -require<double>(to_create_negative_of);
520 }
521
522 boost::any not_(const boost::any& to_create_not_of)
523 {
524         return !require<bool>(to_create_not_of);
525 }
526
527 boost::any multiply(const boost::any& lhs, boost::any& rhs)
528 {
529         return require<double>(lhs) * require<double>(rhs);
530 }
531
532 boost::any divide(const boost::any& lhs, boost::any& rhs)
533 {
534         return require<double>(lhs) / require<double>(rhs);
535 }
536
537 boost::any modulus(const boost::any& lhs, boost::any& rhs)
538 {
539         return
540                         (
541                                         require<double>(lhs).as<int64_t>()
542                                         % require<double>(rhs).as<int64_t>()
543                         ).as<double>();
544 }
545
546 binding<std::wstring> stringify(const boost::any& value)
547 {
548         auto b = as_binding(value);
549
550         if (is<binding<std::wstring>>(b))
551                 return as<binding<std::wstring>>(b);
552         else if (is<binding<double>>(b))
553                 return as<binding<double>>(b).as<std::wstring>();
554         else if (is<binding<bool>>(b))
555                 return as<binding<bool>>(b).as<std::wstring>();
556         else
557                 CASPAR_THROW_EXCEPTION(user_error()
558                                 << msg_info(L"Couldn't stringify " + u16(value.type().name())));
559 }
560
561 boost::any add(const boost::any& lhs, boost::any& rhs)
562 {
563         auto l = as_binding(lhs);
564         auto r = as_binding(rhs);
565
566         // number
567         if (is<binding<double>>(l) && is<binding<double>>(r))
568                 return as<binding<double>>(l) + as<binding<double>>(r);
569         // string
570         else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
571                 return as<binding<std::wstring>>(l) + as<binding<std::wstring>>(r);
572         // mixed types to string and concatenated
573         else
574                 return stringify(lhs) + stringify(rhs);
575 }
576
577 boost::any subtract(const boost::any& lhs, boost::any& rhs)
578 {
579         return require<double>(lhs) - require<double>(rhs);
580 }
581
582 boost::any less(const boost::any& lhs, boost::any& rhs)
583 {
584         return require<double>(lhs) < require<double>(rhs);
585 }
586
587 boost::any less_or_equal(const boost::any& lhs, boost::any& rhs)
588 {
589         return require<double>(lhs) <= require<double>(rhs);
590 }
591
592 boost::any greater(const boost::any& lhs, boost::any& rhs)
593 {
594         return require<double>(lhs) > require<double>(rhs);
595 }
596
597 boost::any greater_or_equal(const boost::any& lhs, boost::any& rhs)
598 {
599         return require<double>(lhs) >= require<double>(rhs);
600 }
601
602 boost::any equal(const boost::any& lhs, boost::any& rhs)
603 {
604         auto l = as_binding(lhs);
605         auto r = as_binding(rhs);
606
607         // number
608         if (is<binding<double>>(l) && is<binding<double>>(r))
609                 return as<binding<double>>(l) == as<binding<double>>(r);
610         // string
611         else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
612                 return as<binding<std::wstring>>(l) == as<binding<std::wstring>>(r);
613         // boolean
614         else
615                 return require<bool>(l) == require<bool>(r);
616 }
617
618 boost::any and_(const boost::any& lhs, boost::any& rhs)
619 {
620         return require<bool>(lhs) && require<bool>(rhs);
621 }
622
623 boost::any or_(const boost::any& lhs, boost::any& rhs)
624 {
625         return require<bool>(lhs) || require<bool>(rhs);
626 }
627
628 template<typename T>
629 binding<T> ternary(
630                 const binding<bool>& condition,
631                 const binding<T>& true_value,
632                 const binding<T>& false_value)
633 {
634         return when(condition).then(true_value).otherwise(false_value);
635 }
636
637 boost::any ternary(
638                 const boost::any& condition,
639                 const boost::any& true_value,
640                 const boost::any& false_value)
641 {
642         auto cond = require<bool>(condition);
643         auto t = as_binding(true_value);
644         auto f = as_binding(false_value);
645         
646         // double
647         if (is<binding<double>>(t) && is<binding<double>>(f))
648                 return ternary(cond, as<binding<double>>(t), as<binding<double>>(f));
649         // string
650         else if (is<binding<std::wstring>>(t) && is<binding<std::wstring>>(f))
651                 return ternary(
652                                 cond,
653                                 as<binding<std::wstring>>(t),
654                                 as<binding<std::wstring>>(f));
655         // bool
656         else
657                 return ternary(cond, require<bool>(t), require<bool>(f));
658 }
659
660 void resolve_operators(int precedence, std::vector<boost::any>& tokens)
661 {
662         for (int i = 0; i < tokens.size(); ++i)
663         {
664                 auto& token = tokens.at(i);
665
666                 if (!is<op>(token))
667                         continue;
668
669                 auto op_token = as<op>(token);
670
671                 if (op_token.precedence != precedence)
672                         continue;
673
674                 int index_after = i + 1;
675                 auto& token_after = tokens.at(index_after);
676
677                 switch (op_token.type)
678                 {
679                 case op::op_type::UNARY:
680                         if (op_token.characters == L"unary-")
681                         {
682                                 tokens.at(i) = negative(token_after);
683                         }
684                         else if (op_token.characters == L"!")
685                         {
686                                 tokens.at(i) = not_(token_after);
687                         }
688
689                         tokens.erase(tokens.begin() + index_after);
690
691                         break;
692                 case op::op_type::BINARY:
693                         {
694                                 auto& token_before = tokens.at(i - 1);
695
696                                 if (op_token.characters == L"*")
697                                         token_before = multiply(token_before, token_after);
698                                 else if (op_token.characters == L"/")
699                                         token_before = divide(token_before, token_after);
700                                 else if (op_token.characters == L"%")
701                                         token_before = modulus(token_before, token_after);
702                                 else if (op_token.characters == L"+")
703                                         token_before = add(token_before, token_after);
704                                 else if (op_token.characters == L"-")
705                                         token_before = subtract(token_before, token_after);
706                                 else if (op_token.characters == L"<")
707                                         token_before = less(token_before, token_after);
708                                 else if (op_token.characters == L"<=")
709                                         token_before = less_or_equal(token_before, token_after);
710                                 else if (op_token.characters == L">")
711                                         token_before = greater(token_before, token_after);
712                                 else if (op_token.characters == L">=")
713                                         token_before = greater_or_equal(token_before, token_after);
714                                 else if (op_token.characters == L"==")
715                                         token_before = equal(token_before, token_after);
716                                 else if (op_token.characters == L"!=")
717                                         token_before = not_(equal(token_before, token_after));
718                                 else if (op_token.characters == L"&&")
719                                         token_before = and_(token_before, token_after);
720                                 else if (op_token.characters == L"||")
721                                         token_before = or_(token_before, token_after);
722                         }
723
724                         tokens.erase(tokens.begin() + i, tokens.begin() + i + 2);
725                         --i;
726
727                         break;
728                 case op::op_type::TERNARY:
729                         if (op_token.characters == L"?")
730                         {
731                                 auto& token_before = tokens.at(i - 1);
732                                 auto& token_colon_operator = tokens.at(i + 2);
733
734                                 if (as<op>(token_colon_operator).characters != L":")
735                                         CASPAR_THROW_EXCEPTION(user_error() << msg_info(
736                                                         L"Expected : as part of ternary expression"));
737
738                                 auto& token_false_value = tokens.at(i + 3);
739                                 token_before = ternary(
740                                                 token_before, token_after, token_false_value);
741                                 tokens.erase(tokens.begin() + i, tokens.begin() + i + 4);
742                                 --i;
743                         }
744
745                         break;
746                 }
747         }
748 }
749
750 boost::any parse_expression(
751                 std::wstring::const_iterator& cursor,
752                 const std::wstring& str,
753                 const variable_repository& var_repo)
754 {
755         std::vector<boost::any> tokens;
756         bool stop = false;
757
758         while (cursor != str.end())
759         {
760                 wchar_t ch = next_non_whitespace(cursor, str, L"Expected expression");
761
762                 switch (ch)
763                 {
764                 case L'0':
765                 case L'1':
766                 case L'2':
767                 case L'3':
768                 case L'4':
769                 case L'5':
770                 case L'6':
771                 case L'7':
772                 case L'8':
773                 case L'9':
774                         tokens.push_back(parse_constant(cursor, str));
775                         break;
776                 case L'+':
777                 case L'-':
778                 case L'*':
779                 case L'/':
780                 case L'%':
781                 case L'<':
782                 case L'>':
783                 case L'!':
784                 case L'=':
785                 case L'|':
786                 case L'&':
787                 case L'?':
788                 case L':':
789                         tokens.push_back(parse_operator(cursor, str));
790                         break;
791                 case L'"':
792                         tokens.push_back(parse_string_literal(cursor, str));
793                         break;
794                 case L'(':
795                         if (!tokens.empty() && is<std::wstring>(tokens.back()))
796                         {
797                                 auto function_name = as<std::wstring>(tokens.back());
798                                 tokens.pop_back();
799                                 tokens.push_back(parse_function(function_name, cursor, str, var_repo));
800                         }
801                         else
802                                 tokens.push_back(parse_parenthesis(cursor, str, var_repo));
803                         break;
804                 case L')':
805                 case L',':
806                         stop = true;
807                         break;
808                 default:
809                         tokens.push_back(parse_variable(cursor, str, var_repo));
810                         break;
811                 }
812
813                 if (stop)
814                         break;
815         }
816
817         if (tokens.empty())
818                 CASPAR_THROW_EXCEPTION(user_error()
819                                 << msg_info(L"Expected expression" + at_position(cursor, str)));
820
821         int precedence = 1;
822
823         while (tokens.size() > 1)
824         {
825                 resolve_operators(precedence++, tokens);
826         }
827
828         return as_binding(tokens.at(0));
829 }
830
831 }}}