2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
4 * This file is part of CasparCG (www.casparcg.com).
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.
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.
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/>.
19 * Author: Helge Norberg, helge.norberg@svt.se
22 #include "../../StdAfx.h"
24 #include "expression_parser.h"
34 #include <boost/any.hpp>
36 #include <common/log.h>
37 #include <common/except.h>
38 #include <common/utf.h>
39 #include <common/tweener.h>
41 namespace caspar { namespace core { namespace scene {
43 wchar_t next_non_whitespace(
44 std::wstring::const_iterator& cursor,
45 const std::wstring& str,
46 const std::wstring& error_if_eof)
48 while (cursor != str.end())
61 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
62 L"Unexpected end of input (" + error_if_eof + L") in " + str));
65 std::wstring at_position(
66 const std::wstring::const_iterator& cursor, const std::wstring& str)
68 int index = static_cast<int>(cursor - str.begin());
70 return L" at index " + boost::lexical_cast<std::wstring>(index)
74 boost::any as_binding(const boost::any& value);
77 binding<T> require(const boost::any& value)
79 auto b = as_binding(value);
81 if (is<binding<T>>(b))
82 return as<binding<T>>(b);
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())));
89 boost::any parse_expression(
90 std::wstring::const_iterator& cursor,
91 const std::wstring& str,
92 const variable_repository& var_repo);
94 boost::any parse_parenthesis(
95 std::wstring::const_iterator& cursor,
96 const std::wstring& str,
97 const variable_repository& var_repo)
99 if (*cursor++ != L'(')
100 CASPAR_THROW_EXCEPTION(user_error()
101 << msg_info(L"Expected (" + at_position(cursor, str)));
103 auto expr = parse_expression(cursor, str, var_repo);
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)));
114 boost::any create_animate_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
116 if (params.size() != 3)
117 CASPAR_THROW_EXCEPTION(user_error()
118 << msg_info(L"animate() function requires three parameters: to_animate, duration, tweener"));
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); });
125 return to_animate.animated(frame_counter, duration, tw);
128 boost::any create_sin_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
130 if (params.size() != 1)
131 CASPAR_THROW_EXCEPTION(user_error()
132 << msg_info(L"sin() function requires one parameters: angle"));
134 auto angle = require<double>(params.at(0));
136 return angle.transformed([](double a) { return std::sin(a); });
139 boost::any create_cos_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
141 if (params.size() != 1)
142 CASPAR_THROW_EXCEPTION(user_error()
143 << msg_info(L"cos() function requires one parameters: angle"));
145 auto angle = require<double>(params.at(0));
147 return angle.transformed([](double a) { return std::cos(a); });
150 boost::any create_abs_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
152 if (params.size() != 1)
153 CASPAR_THROW_EXCEPTION(user_error()
154 << msg_info(L"abs() function requires one parameters: value"));
156 auto val = require<double>(params.at(0));
158 return val.transformed([](double v) { return std::abs(v); });
161 boost::any create_floor_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
163 if (params.size() != 1)
164 CASPAR_THROW_EXCEPTION(user_error()
165 << msg_info(L"floor() function requires one parameters: value"));
167 auto val = require<double>(params.at(0));
169 return val.transformed([](double v) { return std::floor(v); });
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)
178 static std::map<std::wstring, std::function<boost::any (const std::vector<boost::any>& params, const variable_repository& var_repo)>> FUNCTIONS
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 }
187 auto function = FUNCTIONS.find(function_name);
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)));
193 if (*cursor++ != L'(')
194 CASPAR_THROW_EXCEPTION(user_error()
195 << msg_info(L"Expected (" + at_position(cursor, str)));
197 std::vector<boost::any> params;
199 while (cursor != str.end())
201 params.push_back(parse_expression(cursor, str, var_repo));
203 auto next = next_non_whitespace(cursor, str, L"Expected , or )");
207 else if (next != L',')
208 CASPAR_THROW_EXCEPTION(user_error()
209 << msg_info(L"Expected ) or ," + at_position(cursor, str)));
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)));
220 return function->second(params, var_repo);
223 double parse_constant(
224 std::wstring::const_iterator& cursor, const std::wstring& str)
226 std::wstring constant;
228 while (cursor != str.end())
230 wchar_t ch = *cursor;
232 if ((ch >= L'0' && ch <= L'9') || ch == L'.')
240 return boost::lexical_cast<double>(constant);
243 std::wstring parse_string_literal(
244 std::wstring::const_iterator& cursor, const std::wstring& str)
246 std::wstring literal;
248 if (*cursor++ != L'"')
249 CASPAR_THROW_EXCEPTION(user_error()
250 << msg_info(L"Expected (" + at_position(cursor, str)));
252 bool escaping = false;
254 while (cursor != str.end())
256 wchar_t ch = *cursor;
279 return std::move(literal);
297 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
298 L"Unexpected end of input (Expected closing \") in " + str));
301 boost::any parse_variable(
302 std::wstring::const_iterator& cursor,
303 const std::wstring& str,
304 const variable_repository& var_repo)
306 std::wstring variable_name;
308 while (cursor != str.end())
310 wchar_t ch = *cursor;
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'))
324 if (cursor != str.end() && *cursor == L'(')
325 return variable_name;
327 if (variable_name == L"true")
329 else if (variable_name == L"false")
332 variable& var = var_repo(variable_name);
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>();
343 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
344 L"Unhandled variable type of " + variable_name
345 + at_position(cursor, str)));
357 std::wstring characters;
361 op(wchar_t ch, int precedence, op_type type)
362 : characters(1, ch), precedence(precedence), type(type)
366 op(const std::wstring& chs, int precedence, op_type type)
367 : characters(chs), precedence(precedence), type(type)
372 op parse_operator(std::wstring::const_iterator& cursor, const std::wstring& str)
374 static const wchar_t NONE = L' ';
375 wchar_t first = NONE;
377 while (cursor != str.end())
379 wchar_t ch = *cursor;
385 return op(ch, 6, op::op_type::BINARY);
390 return op(ch, 5, op::op_type::BINARY);
394 return op(ch, 15, op::op_type::TERNARY);
397 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
398 L"Did not expect -" + at_position(cursor, str)));
406 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
407 L"Did not expect !" + at_position(cursor, str)));
415 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
416 L"Did not expect <" + at_position(cursor, str)));
424 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
425 L"Did not expect >" + at_position(cursor, str)));
435 return op(L"==", 9, op::op_type::BINARY);
437 else if (first == L'!')
440 return op(L"!=", 9, op::op_type::BINARY);
442 else if (first == L'>')
445 return op(L">=", 8, op::op_type::BINARY);
447 else if (first == L'<')
450 return op(L"<=", 8, op::op_type::BINARY);
452 else if (first == NONE)
458 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
459 L"Did not expect =" + at_position(cursor, str)));
466 return op(L"||", 14, op::op_type::BINARY);
468 else if (first == NONE)
474 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
475 L"Did not expect =" + at_position(cursor, str)));
482 return op(L"&&", 13, op::op_type::BINARY);
484 else if (first == NONE)
490 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
491 L"Did not expect =" + at_position(cursor, str)));
497 return op(L'-', 6, op::op_type::BINARY);
498 else if (first == L'!')
499 return op(L'!', 3, op::op_type::UNARY);
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);
510 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
511 L"Expected second character of operator"
512 + at_position(cursor, str)));
516 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
517 L"Unexpected end of input (Expected operator) in " + str));
520 boost::any as_binding(const boost::any& value)
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);
537 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
538 L"Couldn't detect type of " + u16(value.type().name())));
541 boost::any negative(const boost::any& to_create_negative_of)
543 return -require<double>(to_create_negative_of);
546 boost::any not_(const boost::any& to_create_not_of)
548 return !require<bool>(to_create_not_of);
551 boost::any multiply(const boost::any& lhs, boost::any& rhs)
553 return require<double>(lhs) * require<double>(rhs);
556 boost::any divide(const boost::any& lhs, boost::any& rhs)
558 return require<double>(lhs) / require<double>(rhs);
561 boost::any modulus(const boost::any& lhs, boost::any& rhs)
565 require<double>(lhs).as<int64_t>()
566 % require<double>(rhs).as<int64_t>()
570 binding<std::wstring> stringify(const boost::any& value)
572 auto b = as_binding(value);
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>();
581 CASPAR_THROW_EXCEPTION(user_error()
582 << msg_info(L"Couldn't stringify " + u16(value.type().name())));
585 boost::any add(const boost::any& lhs, boost::any& rhs)
587 auto l = as_binding(lhs);
588 auto r = as_binding(rhs);
591 if (is<binding<double>>(l) && is<binding<double>>(r))
592 return as<binding<double>>(l) + as<binding<double>>(r);
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
598 return stringify(lhs) + stringify(rhs);
601 boost::any subtract(const boost::any& lhs, boost::any& rhs)
603 return require<double>(lhs) - require<double>(rhs);
606 boost::any less(const boost::any& lhs, boost::any& rhs)
608 return require<double>(lhs) < require<double>(rhs);
611 boost::any less_or_equal(const boost::any& lhs, boost::any& rhs)
613 return require<double>(lhs) <= require<double>(rhs);
616 boost::any greater(const boost::any& lhs, boost::any& rhs)
618 return require<double>(lhs) > require<double>(rhs);
621 boost::any greater_or_equal(const boost::any& lhs, boost::any& rhs)
623 return require<double>(lhs) >= require<double>(rhs);
626 boost::any equal(const boost::any& lhs, boost::any& rhs)
628 auto l = as_binding(lhs);
629 auto r = as_binding(rhs);
632 if (is<binding<double>>(l) && is<binding<double>>(r))
633 return as<binding<double>>(l) == as<binding<double>>(r);
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);
639 return require<bool>(l) == require<bool>(r);
642 boost::any and_(const boost::any& lhs, boost::any& rhs)
644 return require<bool>(lhs) && require<bool>(rhs);
647 boost::any or_(const boost::any& lhs, boost::any& rhs)
649 return require<bool>(lhs) || require<bool>(rhs);
654 const binding<bool>& condition,
655 const binding<T>& true_value,
656 const binding<T>& false_value)
658 return when(condition).then(true_value).otherwise(false_value);
662 const boost::any& condition,
663 const boost::any& true_value,
664 const boost::any& false_value)
666 auto cond = require<bool>(condition);
667 auto t = as_binding(true_value);
668 auto f = as_binding(false_value);
671 if (is<binding<double>>(t) && is<binding<double>>(f))
672 return ternary(cond, as<binding<double>>(t), as<binding<double>>(f));
674 else if (is<binding<std::wstring>>(t) && is<binding<std::wstring>>(f))
677 as<binding<std::wstring>>(t),
678 as<binding<std::wstring>>(f));
681 return ternary(cond, require<bool>(t), require<bool>(f));
684 void resolve_operators(int precedence, std::vector<boost::any>& tokens)
686 for (int i = 0; i < tokens.size(); ++i)
688 auto& token = tokens.at(i);
693 auto op_token = as<op>(token);
695 if (op_token.precedence != precedence)
698 int index_after = i + 1;
699 auto& token_after = tokens.at(index_after);
701 switch (op_token.type)
703 case op::op_type::UNARY:
704 if (op_token.characters == L"unary-")
706 tokens.at(i) = negative(token_after);
708 else if (op_token.characters == L"!")
710 tokens.at(i) = not_(token_after);
713 tokens.erase(tokens.begin() + index_after);
716 case op::op_type::BINARY:
718 auto& token_before = tokens.at(i - 1);
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);
748 tokens.erase(tokens.begin() + i, tokens.begin() + i + 2);
752 case op::op_type::TERNARY:
753 if (op_token.characters == L"?")
755 auto& token_before = tokens.at(i - 1);
756 auto& token_colon_operator = tokens.at(i + 2);
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"));
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);
774 boost::any parse_expression(
775 std::wstring::const_iterator& cursor,
776 const std::wstring& str,
777 const variable_repository& var_repo)
779 std::vector<boost::any> tokens;
782 while (cursor != str.end())
784 wchar_t ch = next_non_whitespace(cursor, str, L"Expected expression");
798 tokens.push_back(parse_constant(cursor, str));
813 tokens.push_back(parse_operator(cursor, str));
816 tokens.push_back(parse_string_literal(cursor, str));
819 if (!tokens.empty() && is<std::wstring>(tokens.back()))
821 auto function_name = as<std::wstring>(tokens.back());
823 tokens.push_back(parse_function(function_name, cursor, str, var_repo));
826 tokens.push_back(parse_parenthesis(cursor, str, var_repo));
833 tokens.push_back(parse_variable(cursor, str, var_repo));
842 CASPAR_THROW_EXCEPTION(user_error()
843 << msg_info(L"Expected expression" + at_position(cursor, str)));
847 while (tokens.size() > 1)
849 resolve_operators(precedence++, tokens);
852 return as_binding(tokens.at(0));