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>
35 #include <boost/locale.hpp>
37 #include <common/log.h>
38 #include <common/except.h>
39 #include <common/utf.h>
40 #include <common/tweener.h>
42 namespace caspar { namespace core { namespace scene {
44 wchar_t next_non_whitespace(
45 std::wstring::const_iterator& cursor,
46 const std::wstring& str,
47 const std::wstring& error_if_eof)
49 while (cursor != str.end())
62 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
63 L"Unexpected end of input (" + error_if_eof + L") in " + str));
66 std::wstring at_position(
67 const std::wstring::const_iterator& cursor, const std::wstring& str)
69 int index = static_cast<int>(cursor - str.begin());
71 return L" at index " + boost::lexical_cast<std::wstring>(index)
75 boost::any as_binding(const boost::any& value);
78 binding<T> require(const boost::any& value)
80 auto b = as_binding(value);
82 if (is<binding<T>>(b))
83 return as<binding<T>>(b);
85 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
86 L"Required binding of type " + u16(typeid(T).name())
87 + L" but got " + u16(value.type().name())));
90 boost::any parse_expression(
91 std::wstring::const_iterator& cursor,
92 const std::wstring& str,
93 const variable_repository& var_repo);
95 boost::any parse_parenthesis(
96 std::wstring::const_iterator& cursor,
97 const std::wstring& str,
98 const variable_repository& var_repo)
100 if (*cursor++ != L'(')
101 CASPAR_THROW_EXCEPTION(user_error()
102 << msg_info(L"Expected (" + at_position(cursor, str)));
104 auto expr = parse_expression(cursor, str, var_repo);
106 if (next_non_whitespace(cursor, str, L"Expected )") != L')')
107 CASPAR_THROW_EXCEPTION(user_error()
108 << msg_info(L"Expected )" + at_position(cursor, str)));
115 boost::any create_animate_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
117 if (params.size() != 3)
118 CASPAR_THROW_EXCEPTION(user_error()
119 << msg_info(L"animate() function requires three parameters: to_animate, duration, tweener"));
121 auto to_animate = require<double>(params.at(0));
122 auto frame_counter = var_repo(L"frame").as<double>();
123 auto duration = require<double>(params.at(1));
124 auto tw = require<std::wstring>(params.at(2)).transformed([](const std::wstring& s) { return tweener(s); });
126 return to_animate.animated(frame_counter, duration, tw);
129 boost::any create_sin_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
131 if (params.size() != 1)
132 CASPAR_THROW_EXCEPTION(user_error()
133 << msg_info(L"sin() function requires one parameters: angle"));
135 auto angle = require<double>(params.at(0));
137 return angle.transformed([](double a) { return std::sin(a); });
140 boost::any create_cos_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
142 if (params.size() != 1)
143 CASPAR_THROW_EXCEPTION(user_error()
144 << msg_info(L"cos() function requires one parameters: angle"));
146 auto angle = require<double>(params.at(0));
148 return angle.transformed([](double a) { return std::cos(a); });
151 boost::any create_abs_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
153 if (params.size() != 1)
154 CASPAR_THROW_EXCEPTION(user_error()
155 << msg_info(L"abs() function requires one parameters: value"));
157 auto val = require<double>(params.at(0));
159 return val.transformed([](double v) { return std::abs(v); });
162 boost::any create_floor_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
164 if (params.size() != 1)
165 CASPAR_THROW_EXCEPTION(user_error()
166 << msg_info(L"floor() function requires one parameters: value"));
168 auto val = require<double>(params.at(0));
170 return val.transformed([](double v) { return std::floor(v); });
173 std::locale create_utf_locale()
175 boost::locale::generator gen;
176 gen.categories(boost::locale::codepage_facet);
177 gen.categories(boost::locale::convert_facet);
182 boost::any create_to_lower_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
184 if (params.size() != 1)
185 CASPAR_THROW_EXCEPTION(user_error()
186 << msg_info(L"to_lower() function requires one parameters: str"));
188 auto str = require<std::wstring>(params.at(0));
189 auto locale = create_utf_locale();
191 return str.transformed([=](std::wstring v) { return boost::locale::to_lower(v, locale); });
194 boost::any create_to_upper_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
196 if (params.size() != 1)
197 CASPAR_THROW_EXCEPTION(user_error()
198 << msg_info(L"to_upper() function requires one parameters: str"));
200 auto str = require<std::wstring>(params.at(0));
201 auto locale = create_utf_locale();
203 return str.transformed([=](std::wstring v) { return boost::locale::to_upper(v, locale); });
206 boost::any create_length_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
208 if (params.size() != 1)
209 CASPAR_THROW_EXCEPTION(user_error()
210 << msg_info(L"length() function requires one parameters: str"));
212 auto str = require<std::wstring>(params.at(0));
214 return str.transformed([](std::wstring v) { return static_cast<double>(v.length()); });
217 boost::any parse_function(
218 const std::wstring& function_name,
219 std::wstring::const_iterator& cursor,
220 const std::wstring& str,
221 const variable_repository& var_repo)
223 static std::map<std::wstring, std::function<boost::any (const std::vector<boost::any>& params, const variable_repository& var_repo)>> FUNCTIONS
225 { L"animate", create_animate_function },
226 { L"sin", create_sin_function },
227 { L"cos", create_cos_function },
228 { L"abs", create_abs_function },
229 { L"floor", create_floor_function },
230 { L"to_lower", create_to_lower_function },
231 { L"to_upper", create_to_upper_function },
232 { L"length", create_length_function }
235 auto function = FUNCTIONS.find(function_name);
237 if (function == FUNCTIONS.end())
238 CASPAR_THROW_EXCEPTION(user_error()
239 << msg_info(function_name + L"() is an unknown function" + at_position(cursor, str)));
241 if (*cursor++ != L'(')
242 CASPAR_THROW_EXCEPTION(user_error()
243 << msg_info(L"Expected (" + at_position(cursor, str)));
245 std::vector<boost::any> params;
247 while (cursor != str.end())
249 params.push_back(parse_expression(cursor, str, var_repo));
251 auto next = next_non_whitespace(cursor, str, L"Expected , or )");
255 else if (next != L',')
256 CASPAR_THROW_EXCEPTION(user_error()
257 << msg_info(L"Expected ) or ," + at_position(cursor, str)));
262 if (next_non_whitespace(cursor, str, L"Expected , or )") != L')')
263 CASPAR_THROW_EXCEPTION(user_error()
264 << msg_info(L"Expected ) " + at_position(cursor, str)));
268 return function->second(params, var_repo);
271 double parse_constant(
272 std::wstring::const_iterator& cursor, const std::wstring& str)
274 std::wstring constant;
276 while (cursor != str.end())
278 wchar_t ch = *cursor;
280 if ((ch >= L'0' && ch <= L'9') || ch == L'.')
288 return boost::lexical_cast<double>(constant);
291 std::wstring parse_string_literal(
292 std::wstring::const_iterator& cursor, const std::wstring& str)
294 std::wstring literal;
296 if (*cursor++ != L'"')
297 CASPAR_THROW_EXCEPTION(user_error()
298 << msg_info(L"Expected (" + at_position(cursor, str)));
300 bool escaping = false;
302 while (cursor != str.end())
304 wchar_t ch = *cursor;
327 return std::move(literal);
345 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
346 L"Unexpected end of input (Expected closing \") in " + str));
349 boost::any parse_variable(
350 std::wstring::const_iterator& cursor,
351 const std::wstring& str,
352 const variable_repository& var_repo)
354 std::wstring variable_name;
356 while (cursor != str.end())
358 wchar_t ch = *cursor;
362 || (ch >= L'a' && ch <= L'z')
363 || (ch >= L'A' && ch <= L'Z')
364 || (variable_name.length() > 0 && ch >= L'0' && ch <= L'9'))
372 if (cursor != str.end() && *cursor == L'(')
373 return variable_name;
375 if (variable_name == L"true")
377 else if (variable_name == L"false")
380 variable& var = var_repo(variable_name);
382 if (var.is<double>())
383 return var.as<double>();
384 else if (var.is<int64_t>())
385 return var.as<int64_t>().as<double>();
386 else if (var.is<std::wstring>())
387 return var.as<std::wstring>();
388 else if (var.is<bool>())
389 return var.as<bool>();
391 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
392 L"Unhandled variable type of " + variable_name
393 + at_position(cursor, str)));
405 std::wstring characters;
409 op(wchar_t ch, int precedence, op_type type)
410 : characters(1, ch), precedence(precedence), type(type)
414 op(const std::wstring& chs, int precedence, op_type type)
415 : characters(chs), precedence(precedence), type(type)
420 op parse_operator(std::wstring::const_iterator& cursor, const std::wstring& str)
422 static const wchar_t NONE = L' ';
423 wchar_t first = NONE;
425 while (cursor != str.end())
427 wchar_t ch = *cursor;
433 return op(ch, 6, op::op_type::BINARY);
438 return op(ch, 5, op::op_type::BINARY);
442 return op(ch, 15, op::op_type::TERNARY);
445 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
446 L"Did not expect -" + at_position(cursor, str)));
454 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
455 L"Did not expect !" + at_position(cursor, str)));
463 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
464 L"Did not expect <" + at_position(cursor, str)));
472 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
473 L"Did not expect >" + at_position(cursor, str)));
483 return op(L"==", 9, op::op_type::BINARY);
485 else if (first == L'!')
488 return op(L"!=", 9, op::op_type::BINARY);
490 else if (first == L'>')
493 return op(L">=", 8, op::op_type::BINARY);
495 else if (first == L'<')
498 return op(L"<=", 8, op::op_type::BINARY);
500 else if (first == NONE)
506 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
507 L"Did not expect =" + at_position(cursor, str)));
514 return op(L"||", 14, op::op_type::BINARY);
516 else if (first == NONE)
522 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
523 L"Did not expect =" + at_position(cursor, str)));
530 return op(L"&&", 13, op::op_type::BINARY);
532 else if (first == NONE)
538 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
539 L"Did not expect =" + at_position(cursor, str)));
545 return op(L'-', 6, op::op_type::BINARY);
546 else if (first == L'!')
547 return op(L'!', 3, op::op_type::UNARY);
550 return op(L'<', 8, op::op_type::BINARY);
551 else if (first == L'>')
552 return op(L'>', 8, op::op_type::BINARY);
553 else if (first == L'-')
554 return op(L"unary-", 3, op::op_type::UNARY);
555 else if (first == L'!')
556 return op(L'!', 3, op::op_type::UNARY);
558 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
559 L"Expected second character of operator"
560 + at_position(cursor, str)));
564 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
565 L"Unexpected end of input (Expected operator) in " + str));
568 boost::any as_binding(const boost::any& value)
570 // Wrap supported constants as bindings
571 if (is<double>(value))
572 return binding<double>(as<double>(value));
573 else if (is<bool>(value))
574 return binding<bool>(as<bool>(value));
575 else if (is<std::wstring>(value))
576 return binding<std::wstring>(as<std::wstring>(value));
577 // Already one of the supported binding types
578 else if (is<binding<double>>(value))
579 return as<binding<double>>(value);
580 else if (is<binding<bool>>(value))
581 return as<binding<bool>>(value);
582 else if (is<binding<std::wstring>>(value))
583 return as<binding<std::wstring>>(value);
585 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
586 L"Couldn't detect type of " + u16(value.type().name())));
589 boost::any negative(const boost::any& to_create_negative_of)
591 return -require<double>(to_create_negative_of);
594 boost::any not_(const boost::any& to_create_not_of)
596 return !require<bool>(to_create_not_of);
599 boost::any multiply(const boost::any& lhs, boost::any& rhs)
601 return require<double>(lhs) * require<double>(rhs);
604 boost::any divide(const boost::any& lhs, boost::any& rhs)
606 return require<double>(lhs) / require<double>(rhs);
609 boost::any modulus(const boost::any& lhs, boost::any& rhs)
613 require<double>(lhs).as<int64_t>()
614 % require<double>(rhs).as<int64_t>()
618 binding<std::wstring> stringify(const boost::any& value)
620 auto b = as_binding(value);
622 if (is<binding<std::wstring>>(b))
623 return as<binding<std::wstring>>(b);
624 else if (is<binding<double>>(b))
625 return as<binding<double>>(b).as<std::wstring>();
626 else if (is<binding<bool>>(b))
627 return as<binding<bool>>(b).as<std::wstring>();
629 CASPAR_THROW_EXCEPTION(user_error()
630 << msg_info(L"Couldn't stringify " + u16(value.type().name())));
633 boost::any add(const boost::any& lhs, boost::any& rhs)
635 auto l = as_binding(lhs);
636 auto r = as_binding(rhs);
639 if (is<binding<double>>(l) && is<binding<double>>(r))
640 return as<binding<double>>(l) + as<binding<double>>(r);
642 else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
643 return as<binding<std::wstring>>(l) + as<binding<std::wstring>>(r);
644 // mixed types to string and concatenated
646 return stringify(lhs) + stringify(rhs);
649 boost::any subtract(const boost::any& lhs, boost::any& rhs)
651 return require<double>(lhs) - require<double>(rhs);
654 boost::any less(const boost::any& lhs, boost::any& rhs)
656 return require<double>(lhs) < require<double>(rhs);
659 boost::any less_or_equal(const boost::any& lhs, boost::any& rhs)
661 return require<double>(lhs) <= require<double>(rhs);
664 boost::any greater(const boost::any& lhs, boost::any& rhs)
666 return require<double>(lhs) > require<double>(rhs);
669 boost::any greater_or_equal(const boost::any& lhs, boost::any& rhs)
671 return require<double>(lhs) >= require<double>(rhs);
674 boost::any equal(const boost::any& lhs, boost::any& rhs)
676 auto l = as_binding(lhs);
677 auto r = as_binding(rhs);
680 if (is<binding<double>>(l) && is<binding<double>>(r))
681 return as<binding<double>>(l) == as<binding<double>>(r);
683 else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
684 return as<binding<std::wstring>>(l) == as<binding<std::wstring>>(r);
687 return require<bool>(l) == require<bool>(r);
690 boost::any and_(const boost::any& lhs, boost::any& rhs)
692 return require<bool>(lhs) && require<bool>(rhs);
695 boost::any or_(const boost::any& lhs, boost::any& rhs)
697 return require<bool>(lhs) || require<bool>(rhs);
702 const binding<bool>& condition,
703 const binding<T>& true_value,
704 const binding<T>& false_value)
706 return when(condition).then(true_value).otherwise(false_value);
710 const boost::any& condition,
711 const boost::any& true_value,
712 const boost::any& false_value)
714 auto cond = require<bool>(condition);
715 auto t = as_binding(true_value);
716 auto f = as_binding(false_value);
719 if (is<binding<double>>(t) && is<binding<double>>(f))
720 return ternary(cond, as<binding<double>>(t), as<binding<double>>(f));
722 else if (is<binding<std::wstring>>(t) && is<binding<std::wstring>>(f))
725 as<binding<std::wstring>>(t),
726 as<binding<std::wstring>>(f));
729 return ternary(cond, require<bool>(t), require<bool>(f));
732 void resolve_operators(int precedence, std::vector<boost::any>& tokens)
734 for (int i = 0; i < tokens.size(); ++i)
736 auto& token = tokens.at(i);
741 auto op_token = as<op>(token);
743 if (op_token.precedence != precedence)
746 int index_after = i + 1;
747 auto& token_after = tokens.at(index_after);
749 switch (op_token.type)
751 case op::op_type::UNARY:
752 if (op_token.characters == L"unary-")
754 tokens.at(i) = negative(token_after);
756 else if (op_token.characters == L"!")
758 tokens.at(i) = not_(token_after);
761 tokens.erase(tokens.begin() + index_after);
764 case op::op_type::BINARY:
766 auto& token_before = tokens.at(i - 1);
768 if (op_token.characters == L"*")
769 token_before = multiply(token_before, token_after);
770 else if (op_token.characters == L"/")
771 token_before = divide(token_before, token_after);
772 else if (op_token.characters == L"%")
773 token_before = modulus(token_before, token_after);
774 else if (op_token.characters == L"+")
775 token_before = add(token_before, token_after);
776 else if (op_token.characters == L"-")
777 token_before = subtract(token_before, token_after);
778 else if (op_token.characters == L"<")
779 token_before = less(token_before, token_after);
780 else if (op_token.characters == L"<=")
781 token_before = less_or_equal(token_before, token_after);
782 else if (op_token.characters == L">")
783 token_before = greater(token_before, token_after);
784 else if (op_token.characters == L">=")
785 token_before = greater_or_equal(token_before, token_after);
786 else if (op_token.characters == L"==")
787 token_before = equal(token_before, token_after);
788 else if (op_token.characters == L"!=")
789 token_before = not_(equal(token_before, token_after));
790 else if (op_token.characters == L"&&")
791 token_before = and_(token_before, token_after);
792 else if (op_token.characters == L"||")
793 token_before = or_(token_before, token_after);
796 tokens.erase(tokens.begin() + i, tokens.begin() + i + 2);
800 case op::op_type::TERNARY:
801 if (op_token.characters == L"?")
803 auto& token_before = tokens.at(i - 1);
804 auto& token_colon_operator = tokens.at(i + 2);
806 if (as<op>(token_colon_operator).characters != L":")
807 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
808 L"Expected : as part of ternary expression"));
810 auto& token_false_value = tokens.at(i + 3);
811 token_before = ternary(
812 token_before, token_after, token_false_value);
813 tokens.erase(tokens.begin() + i, tokens.begin() + i + 4);
822 boost::any parse_expression(
823 std::wstring::const_iterator& cursor,
824 const std::wstring& str,
825 const variable_repository& var_repo)
827 std::vector<boost::any> tokens;
830 while (cursor != str.end())
832 wchar_t ch = next_non_whitespace(cursor, str, L"Expected expression");
846 tokens.push_back(parse_constant(cursor, str));
861 tokens.push_back(parse_operator(cursor, str));
864 tokens.push_back(parse_string_literal(cursor, str));
867 if (!tokens.empty() && is<std::wstring>(tokens.back()))
869 auto function_name = as<std::wstring>(tokens.back());
871 tokens.push_back(parse_function(function_name, cursor, str, var_repo));
874 tokens.push_back(parse_parenthesis(cursor, str, var_repo));
881 tokens.push_back(parse_variable(cursor, str, var_repo));
890 CASPAR_THROW_EXCEPTION(user_error()
891 << msg_info(L"Expected expression" + at_position(cursor, str)));
895 while (tokens.size() > 1)
897 resolve_operators(precedence++, tokens);
900 return as_binding(tokens.at(0));