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