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"
33 #include <boost/any.hpp>
35 #include <common/log.h>
36 #include <common/except.h>
37 #include <common/utf.h>
38 #include <common/tweener.h>
40 namespace caspar { namespace core { namespace scene {
42 wchar_t next_non_whitespace(
43 std::wstring::const_iterator& cursor,
44 const std::wstring& str,
45 const std::wstring& error_if_eof)
47 while (cursor != str.end())
60 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
61 L"Unexpected end of input (" + error_if_eof + L") in " + str));
64 std::wstring at_position(
65 const std::wstring::const_iterator& cursor, const std::wstring& str)
67 int index = static_cast<int>(cursor - str.begin());
69 return L" at index " + boost::lexical_cast<std::wstring>(index)
74 binding<T> require(const boost::any& value)
76 auto b = as_binding(value);
78 if (is<binding<T>>(b))
79 return as<binding<T>>(b);
81 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
82 L"Required binding of type " + u16(typeid(T).name())
83 + L" but got " + u16(value.type().name())));
86 boost::any parse_expression(
87 std::wstring::const_iterator& cursor,
88 const std::wstring& str,
89 const variable_repository& var_repo);
91 boost::any parse_parenthesis(
92 std::wstring::const_iterator& cursor,
93 const std::wstring& str,
94 const variable_repository& var_repo)
96 if (*cursor++ != L'(')
97 CASPAR_THROW_EXCEPTION(user_error()
98 << msg_info(L"Expected (" + at_position(cursor, str)));
100 auto expr = parse_expression(cursor, str, var_repo);
102 if (next_non_whitespace(cursor, str, L"Expected )") != L')')
103 CASPAR_THROW_EXCEPTION(user_error()
104 << msg_info(L"Expected )" + at_position(cursor, str)));
111 boost::any create_animate_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
113 if (params.size() != 3)
114 CASPAR_THROW_EXCEPTION(user_error()
115 << msg_info(L"animate() function requires three parameters: to_animate, duration, tweener"));
117 auto to_animate = require<double>(params.at(0));
118 auto frame_counter = var_repo(L"frame").as<int64_t>().as<double>();
119 auto duration = require<double>(params.at(1));
120 auto tw = require<std::wstring>(params.at(2)).transformed([](const std::wstring& s) { return tweener(s); });
122 return to_animate.animated(frame_counter, duration, tw);
125 boost::any parse_function(
126 const std::wstring& function_name,
127 std::wstring::const_iterator& cursor,
128 const std::wstring& str,
129 const variable_repository& var_repo)
131 static std::map<std::wstring, std::function<boost::any (const std::vector<boost::any>& params, const variable_repository& var_repo)>> FUNCTIONS
133 {L"animate", create_animate_function }
136 auto function = FUNCTIONS.find(function_name);
138 if (function == FUNCTIONS.end())
139 CASPAR_THROW_EXCEPTION(user_error()
140 << msg_info(function_name + L"() is an unknown function" + at_position(cursor, str)));
142 if (*cursor++ != L'(')
143 CASPAR_THROW_EXCEPTION(user_error()
144 << msg_info(L"Expected (" + at_position(cursor, str)));
146 std::vector<boost::any> params;
148 while (cursor != str.end())
150 params.push_back(parse_expression(cursor, str, var_repo));
152 auto next = next_non_whitespace(cursor, str, L"Expected , or )");
156 else if (next != L',')
157 CASPAR_THROW_EXCEPTION(user_error()
158 << msg_info(L"Expected ) or ," + at_position(cursor, str)));
163 if (next_non_whitespace(cursor, str, L"Expected , or )") != L')')
164 CASPAR_THROW_EXCEPTION(user_error()
165 << msg_info(L"Expected ) " + at_position(cursor, str)));
169 return function->second(params, var_repo);
172 double parse_constant(
173 std::wstring::const_iterator& cursor, const std::wstring& str)
175 std::wstring constant;
177 while (cursor != str.end())
179 wchar_t ch = *cursor;
181 if ((ch >= L'0' && ch <= L'9') || ch == L'.')
189 return boost::lexical_cast<double>(constant);
192 std::wstring parse_string_literal(
193 std::wstring::const_iterator& cursor, const std::wstring& str)
195 std::wstring literal;
197 if (*cursor++ != L'"')
198 CASPAR_THROW_EXCEPTION(user_error()
199 << msg_info(L"Expected (" + at_position(cursor, str)));
201 bool escaping = false;
203 while (cursor != str.end())
205 wchar_t ch = *cursor;
228 return std::move(literal);
246 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
247 L"Unexpected end of input (Expected closing \") in " + str));
250 boost::any parse_variable(
251 std::wstring::const_iterator& cursor,
252 const std::wstring& str,
253 const variable_repository& var_repo)
255 std::wstring variable_name;
257 while (cursor != str.end())
259 wchar_t ch = *cursor;
263 || (ch >= L'a' && ch <= L'z')
264 || (ch >= L'A' && ch <= L'Z')
265 || (variable_name.length() > 0 && ch >= L'0' && ch <= L'9'))
273 if (cursor != str.end() && *cursor == L'(')
274 return variable_name;
276 if (variable_name == L"true")
278 else if (variable_name == L"false")
281 variable& var = var_repo(variable_name);
283 if (var.is<double>())
284 return var.as<double>();
285 else if (var.is<int64_t>())
286 return var.as<int64_t>().as<double>();
287 else if (var.is<std::wstring>())
288 return var.as<std::wstring>();
289 else if (var.is<bool>())
290 return var.as<bool>();
292 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
293 L"Unhandled variable type of " + variable_name
294 + at_position(cursor, str)));
306 std::wstring characters;
310 op(wchar_t ch, int precedence, op_type type)
311 : characters(1, ch), precedence(precedence), type(type)
315 op(const std::wstring& chs, int precedence, op_type type)
316 : characters(chs), precedence(precedence), type(type)
321 op parse_operator(std::wstring::const_iterator& cursor, const std::wstring& str)
323 static const wchar_t NONE = L' ';
324 wchar_t first = NONE;
326 while (cursor != str.end())
328 wchar_t ch = *cursor;
334 return op(ch, 6, op::op_type::BINARY);
339 return op(ch, 5, op::op_type::BINARY);
343 return op(ch, 15, op::op_type::TERNARY);
346 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
347 L"Did not expect -" + at_position(cursor, str)));
355 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
356 L"Did not expect !" + at_position(cursor, str)));
364 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
365 L"Did not expect <" + at_position(cursor, str)));
373 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
374 L"Did not expect >" + at_position(cursor, str)));
384 return op(L"==", 9, op::op_type::BINARY);
386 else if (first == L'!')
389 return op(L"!=", 9, op::op_type::BINARY);
391 else if (first == L'>')
394 return op(L">=", 8, op::op_type::BINARY);
396 else if (first == L'<')
399 return op(L"<=", 8, op::op_type::BINARY);
401 else if (first == NONE)
407 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
408 L"Did not expect =" + at_position(cursor, str)));
415 return op(L"||", 14, op::op_type::BINARY);
417 else if (first == NONE)
423 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
424 L"Did not expect =" + at_position(cursor, str)));
431 return op(L"&&", 13, op::op_type::BINARY);
433 else if (first == NONE)
439 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
440 L"Did not expect =" + at_position(cursor, str)));
446 return op(L'-', 6, op::op_type::BINARY);
447 else if (first == L'!')
448 return op(L'!', 3, op::op_type::UNARY);
451 return op(L'<', 8, op::op_type::BINARY);
452 else if (first == L'>')
453 return op(L'>', 8, op::op_type::BINARY);
454 else if (first == L'-')
455 return op(L"unary-", 3, op::op_type::UNARY);
456 else if (first == L'!')
457 return op(L'!', 3, op::op_type::UNARY);
459 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
460 L"Expected second character of operator"
461 + at_position(cursor, str)));
465 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
466 L"Unexpected end of input (Expected operator) in " + str));
469 boost::any as_binding(const boost::any& value)
471 // Wrap supported constants as bindings
472 if (is<double>(value))
473 return binding<double>(as<double>(value));
474 else if (is<bool>(value))
475 return binding<bool>(as<bool>(value));
476 else if (is<std::wstring>(value))
477 return binding<std::wstring>(as<std::wstring>(value));
478 // Already one of the supported binding types
479 else if (is<binding<double>>(value))
480 return as<binding<double>>(value);
481 else if (is<binding<bool>>(value))
482 return as<binding<bool>>(value);
483 else if (is<binding<std::wstring>>(value))
484 return as<binding<std::wstring>>(value);
486 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
487 L"Couldn't detect type of " + u16(value.type().name())));
490 boost::any negative(const boost::any& to_create_negative_of)
492 return -require<double>(to_create_negative_of);
495 boost::any not_(const boost::any& to_create_not_of)
497 return !require<bool>(to_create_not_of);
500 boost::any multiply(const boost::any& lhs, boost::any& rhs)
502 return require<double>(lhs) * require<double>(rhs);
505 boost::any divide(const boost::any& lhs, boost::any& rhs)
507 return require<double>(lhs) / require<double>(rhs);
510 boost::any modulus(const boost::any& lhs, boost::any& rhs)
514 require<double>(lhs).as<int64_t>()
515 % require<double>(rhs).as<int64_t>()
519 binding<std::wstring> stringify(const boost::any& value)
521 auto b = as_binding(value);
523 if (is<binding<std::wstring>>(b))
524 return as<binding<std::wstring>>(b);
525 else if (is<binding<double>>(b))
526 return as<binding<double>>(b).as<std::wstring>();
527 else if (is<binding<bool>>(b))
528 return as<binding<bool>>(b).as<std::wstring>();
530 CASPAR_THROW_EXCEPTION(user_error()
531 << msg_info(L"Couldn't stringify " + u16(value.type().name())));
534 boost::any add(const boost::any& lhs, boost::any& rhs)
536 auto l = as_binding(lhs);
537 auto r = as_binding(rhs);
540 if (is<binding<double>>(l) && is<binding<double>>(r))
541 return as<binding<double>>(l) + as<binding<double>>(r);
543 else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
544 return as<binding<std::wstring>>(l) + as<binding<std::wstring>>(r);
545 // mixed types to string and concatenated
547 return stringify(lhs) + stringify(rhs);
550 boost::any subtract(const boost::any& lhs, boost::any& rhs)
552 return require<double>(lhs) - require<double>(rhs);
555 boost::any less(const boost::any& lhs, boost::any& rhs)
557 return require<double>(lhs) < require<double>(rhs);
560 boost::any less_or_equal(const boost::any& lhs, boost::any& rhs)
562 return require<double>(lhs) <= require<double>(rhs);
565 boost::any greater(const boost::any& lhs, boost::any& rhs)
567 return require<double>(lhs) > require<double>(rhs);
570 boost::any greater_or_equal(const boost::any& lhs, boost::any& rhs)
572 return require<double>(lhs) >= require<double>(rhs);
575 boost::any equal(const boost::any& lhs, boost::any& rhs)
577 auto l = as_binding(lhs);
578 auto r = as_binding(rhs);
581 if (is<binding<double>>(l) && is<binding<double>>(r))
582 return as<binding<double>>(l) == as<binding<double>>(r);
584 else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
585 return as<binding<std::wstring>>(l) == as<binding<std::wstring>>(r);
588 return require<bool>(l) == require<bool>(r);
591 boost::any and_(const boost::any& lhs, boost::any& rhs)
593 return require<bool>(lhs) && require<bool>(rhs);
596 boost::any or_(const boost::any& lhs, boost::any& rhs)
598 return require<bool>(lhs) || require<bool>(rhs);
603 const binding<bool>& condition,
604 const binding<T>& true_value,
605 const binding<T>& false_value)
607 return when(condition).then(true_value).otherwise(false_value);
611 const boost::any& condition,
612 const boost::any& true_value,
613 const boost::any& false_value)
615 auto cond = require<bool>(condition);
616 auto t = as_binding(true_value);
617 auto f = as_binding(false_value);
620 if (is<binding<double>>(t) && is<binding<double>>(f))
621 return ternary(cond, as<binding<double>>(t), as<binding<double>>(f));
623 else if (is<binding<std::wstring>>(t) && is<binding<std::wstring>>(f))
626 as<binding<std::wstring>>(t),
627 as<binding<std::wstring>>(f));
630 return ternary(cond, require<bool>(t), require<bool>(f));
633 void resolve_operators(int precedence, std::vector<boost::any>& tokens)
635 for (int i = 0; i < tokens.size(); ++i)
637 auto& token = tokens.at(i);
642 auto op_token = as<op>(token);
644 if (op_token.precedence != precedence)
647 int index_after = i + 1;
648 auto& token_after = tokens.at(index_after);
650 switch (op_token.type)
652 case op::op_type::UNARY:
653 if (op_token.characters == L"unary-")
655 tokens.at(i) = negative(token_after);
657 else if (op_token.characters == L"!")
659 tokens.at(i) = not_(token_after);
662 tokens.erase(tokens.begin() + index_after);
665 case op::op_type::BINARY:
667 auto& token_before = tokens.at(i - 1);
669 if (op_token.characters == L"*")
670 token_before = multiply(token_before, token_after);
671 else if (op_token.characters == L"/")
672 token_before = divide(token_before, token_after);
673 else if (op_token.characters == L"%")
674 token_before = modulus(token_before, token_after);
675 else if (op_token.characters == L"+")
676 token_before = add(token_before, token_after);
677 else if (op_token.characters == L"-")
678 token_before = subtract(token_before, token_after);
679 else if (op_token.characters == L"<")
680 token_before = less(token_before, token_after);
681 else if (op_token.characters == L"<=")
682 token_before = less_or_equal(token_before, token_after);
683 else if (op_token.characters == L">")
684 token_before = greater(token_before, token_after);
685 else if (op_token.characters == L">=")
686 token_before = greater_or_equal(token_before, token_after);
687 else if (op_token.characters == L"==")
688 token_before = equal(token_before, token_after);
689 else if (op_token.characters == L"!=")
690 token_before = not_(equal(token_before, token_after));
691 else if (op_token.characters == L"&&")
692 token_before = and_(token_before, token_after);
693 else if (op_token.characters == L"||")
694 token_before = or_(token_before, token_after);
697 tokens.erase(tokens.begin() + i, tokens.begin() + i + 2);
701 case op::op_type::TERNARY:
702 if (op_token.characters == L"?")
704 auto& token_before = tokens.at(i - 1);
705 auto& token_colon_operator = tokens.at(i + 2);
707 if (as<op>(token_colon_operator).characters != L":")
708 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
709 L"Expected : as part of ternary expression"));
711 auto& token_false_value = tokens.at(i + 3);
712 token_before = ternary(
713 token_before, token_after, token_false_value);
714 tokens.erase(tokens.begin() + i, tokens.begin() + i + 4);
723 boost::any parse_expression(
724 std::wstring::const_iterator& cursor,
725 const std::wstring& str,
726 const variable_repository& var_repo)
728 std::vector<boost::any> tokens;
731 while (cursor != str.end())
733 wchar_t ch = next_non_whitespace(cursor, str, L"Expected expression");
747 tokens.push_back(parse_constant(cursor, str));
762 tokens.push_back(parse_operator(cursor, str));
765 tokens.push_back(parse_string_literal(cursor, str));
768 if (!tokens.empty() && is<std::wstring>(tokens.back()))
770 auto function_name = as<std::wstring>(tokens.back());
772 tokens.push_back(parse_function(function_name, cursor, str, var_repo));
775 tokens.push_back(parse_parenthesis(cursor, str, var_repo));
782 tokens.push_back(parse_variable(cursor, str, var_repo));
791 CASPAR_THROW_EXCEPTION(user_error()
792 << msg_info(L"Expected expression" + at_position(cursor, str)));
796 while (tokens.size() > 1)
798 resolve_operators(precedence++, tokens);
801 return as_binding(tokens.at(0));