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>
39 namespace caspar { namespace core { namespace scene {
41 wchar_t next_non_whitespace(
42 std::wstring::const_iterator& cursor,
43 const std::wstring& str,
44 const std::wstring& error_if_eof)
46 while (cursor != str.end())
59 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
60 L"Unexpected end of input (" + error_if_eof + L") in " + str));
63 std::wstring at_position(
64 const std::wstring::const_iterator& cursor, const std::wstring& str)
66 int index = static_cast<int>(cursor - str.begin());
68 return L" at index " + boost::lexical_cast<std::wstring>(index)
72 boost::any parse_expression(
73 std::wstring::const_iterator& cursor,
74 const std::wstring& str,
75 const variable_repository& var_repo);
77 boost::any parse_parenthesis(
78 std::wstring::const_iterator& cursor,
79 const std::wstring& str,
80 const variable_repository& var_repo)
82 if (*cursor++ != L'(')
83 CASPAR_THROW_EXCEPTION(caspar_exception()
84 << msg_info(L"Expected (" + at_position(cursor, str)));
86 auto expr = parse_expression(cursor, str, var_repo);
88 if (*cursor++ != L')')
89 CASPAR_THROW_EXCEPTION(caspar_exception()
90 << msg_info(L"Expected )" + at_position(cursor, str)));
95 double parse_constant(
96 std::wstring::const_iterator& cursor, const std::wstring& str)
98 std::wstring constant;
100 while (cursor != str.end())
102 wchar_t ch = *cursor;
104 if ((ch >= L'0' && ch <= L'9') || ch == L'.')
112 return boost::lexical_cast<double>(constant);
115 std::wstring parse_string_literal(
116 std::wstring::const_iterator& cursor, const std::wstring& str)
118 std::wstring literal;
120 if (*cursor++ != L'"')
121 CASPAR_THROW_EXCEPTION(caspar_exception()
122 << msg_info(L"Expected (" + at_position(cursor, str)));
124 bool escaping = false;
126 while (cursor != str.end())
128 wchar_t ch = *cursor;
151 return std::move(literal);
169 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
170 L"Unexpected end of input (Expected closing \") in " + str));
173 boost::any parse_variable(
174 std::wstring::const_iterator& cursor,
175 const std::wstring& str,
176 const variable_repository& var_repo)
178 std::wstring variable_name;
180 while (cursor != str.end())
182 wchar_t ch = *cursor;
186 || (ch >= L'a' && ch <= L'z')
187 || (ch >= L'A' && ch <= L'Z'))
195 if (variable_name == L"true")
197 else if (variable_name == L"false")
200 variable& var = var_repo(variable_name);
202 if (var.is<double>())
203 return var.as<double>();
204 else if (var.is<int64_t>())
205 return var.as<int64_t>().as<double>();
206 else if (var.is<std::wstring>())
207 return var.as<std::wstring>();
208 else if (var.is<bool>())
209 return var.as<bool>();
211 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
212 L"Unhandled variable type of " + variable_name
213 + at_position(cursor, str)));
225 std::wstring characters;
229 op(wchar_t ch, int precedence, op_type type)
230 : characters(1, ch), precedence(precedence), type(type)
234 op(const std::wstring& chs, int precedence, op_type type)
235 : characters(chs), precedence(precedence), type(type)
240 op parse_operator(std::wstring::const_iterator& cursor, const std::wstring& str)
242 static const wchar_t NONE = L' ';
243 wchar_t first = NONE;
245 while (cursor != str.end())
247 wchar_t ch = *cursor;
253 return op(ch, 6, op::op_type::BINARY);
258 return op(ch, 5, op::op_type::BINARY);
262 return op(ch, 15, op::op_type::TERNARY);
265 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
266 L"Did not expect -" + at_position(cursor, str)));
274 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
275 L"Did not expect !" + at_position(cursor, str)));
283 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
284 L"Did not expect <" + at_position(cursor, str)));
292 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
293 L"Did not expect >" + at_position(cursor, str)));
303 return op(L"==", 9, op::op_type::BINARY);
305 else if (first == L'!')
308 return op(L"!=", 9, op::op_type::BINARY);
310 else if (first == L'>')
313 return op(L">=", 8, op::op_type::BINARY);
315 else if (first == L'<')
318 return op(L"<=", 8, op::op_type::BINARY);
320 else if (first == NONE)
326 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
327 L"Did not expect =" + at_position(cursor, str)));
334 return op(L"||", 14, op::op_type::BINARY);
336 else if (first == NONE)
342 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
343 L"Did not expect =" + at_position(cursor, str)));
350 return op(L"&&", 13, op::op_type::BINARY);
352 else if (first == NONE)
358 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
359 L"Did not expect =" + at_position(cursor, str)));
365 return op(L'-', 6, op::op_type::BINARY);
366 else if (first == L'!')
367 return op(L'!', 3, op::op_type::UNARY);
370 return op(L'<', 8, op::op_type::BINARY);
371 else if (first == L'>')
372 return op(L'>', 8, op::op_type::BINARY);
373 else if (first == L'-')
374 return op(L"unary-", 3, op::op_type::UNARY);
375 else if (first == L'!')
376 return op(L'!', 3, op::op_type::UNARY);
378 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
379 L"Expected second character of operator"
380 + at_position(cursor, str)));
384 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
385 L"Unexpected end of input (Expected operator) in " + str));
389 T as(const boost::any& value)
391 return boost::any_cast<T>(value);
394 boost::any as_binding(const boost::any& value)
396 // Wrap supported constants as bindings
397 if (is<double>(value))
398 return binding<double>(as<double>(value));
399 else if (is<bool>(value))
400 return binding<bool>(as<bool>(value));
401 else if (is<std::wstring>(value))
402 return binding<std::wstring>(as<std::wstring>(value));
403 // Already one of the supported binding types
404 else if (is<binding<double>>(value))
405 return as<binding<double>>(value);
406 else if (is<binding<bool>>(value))
407 return as<binding<bool>>(value);
408 else if (is<binding<std::wstring>>(value))
409 return as<binding<std::wstring>>(value);
411 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
412 L"Couldn't detect type of " + u16(value.type().name())));
416 binding<T> require(const boost::any& value)
418 auto b = as_binding(value);
420 if (is<binding<T>>(b))
421 return as<binding<T>>(b);
423 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
424 L"Required binding of type " + u16(typeid(T).name())
425 + L" but got " + u16(value.type().name())));
428 boost::any negative(const boost::any& to_create_negative_of)
430 return -require<double>(to_create_negative_of);
433 boost::any not_(const boost::any& to_create_not_of)
435 return !require<bool>(to_create_not_of);
438 boost::any multiply(const boost::any& lhs, boost::any& rhs)
440 return require<double>(lhs) * require<double>(rhs);
443 boost::any divide(const boost::any& lhs, boost::any& rhs)
445 return require<double>(lhs) / require<double>(rhs);
448 boost::any modulus(const boost::any& lhs, boost::any& rhs)
452 require<double>(lhs).as<int64_t>()
453 % require<double>(rhs).as<int64_t>()
457 binding<std::wstring> stringify(const boost::any& value)
459 auto b = as_binding(value);
461 if (is<binding<std::wstring>>(b))
462 return as<binding<std::wstring>>(b);
463 else if (is<binding<double>>(b))
464 return as<binding<double>>(b).as<std::wstring>();
465 else if (is<binding<bool>>(b))
466 return as<binding<bool>>(b).as<std::wstring>();
468 CASPAR_THROW_EXCEPTION(caspar_exception()
469 << msg_info(L"Couldn't stringify " + u16(value.type().name())));
472 boost::any add(const boost::any& lhs, boost::any& rhs)
474 auto l = as_binding(lhs);
475 auto r = as_binding(rhs);
478 if (is<binding<double>>(l) && is<binding<double>>(r))
479 return as<binding<double>>(l) + as<binding<double>>(r);
481 else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
482 return as<binding<std::wstring>>(l) + as<binding<std::wstring>>(r);
483 // mixed types to string and concatenated
485 return stringify(lhs) + stringify(rhs);
488 boost::any subtract(const boost::any& lhs, boost::any& rhs)
490 return require<double>(lhs) - require<double>(rhs);
493 boost::any less(const boost::any& lhs, boost::any& rhs)
495 return require<double>(lhs) < require<double>(rhs);
498 boost::any less_or_equal(const boost::any& lhs, boost::any& rhs)
500 return require<double>(lhs) <= require<double>(rhs);
503 boost::any greater(const boost::any& lhs, boost::any& rhs)
505 return require<double>(lhs) > require<double>(rhs);
508 boost::any greater_or_equal(const boost::any& lhs, boost::any& rhs)
510 return require<double>(lhs) >= require<double>(rhs);
513 boost::any equal(const boost::any& lhs, boost::any& rhs)
515 auto l = as_binding(lhs);
516 auto r = as_binding(rhs);
519 if (is<binding<double>>(l) && is<binding<double>>(r))
520 return as<binding<double>>(l) == as<binding<double>>(r);
522 else if (is<binding<std::wstring>>(l) && is<binding<std::wstring>>(r))
523 return as<binding<std::wstring>>(l) == as<binding<std::wstring>>(r);
526 return require<bool>(l) == require<bool>(r);
529 boost::any and_(const boost::any& lhs, boost::any& rhs)
531 return require<bool>(lhs) && require<bool>(rhs);
534 boost::any or_(const boost::any& lhs, boost::any& rhs)
536 return require<bool>(lhs) || require<bool>(rhs);
541 const binding<bool>& condition,
542 const binding<T>& true_value,
543 const binding<T>& false_value)
545 return when(condition).then(true_value).otherwise(false_value);
549 const boost::any& condition,
550 const boost::any& true_value,
551 const boost::any& false_value)
553 auto cond = require<bool>(condition);
554 auto t = as_binding(true_value);
555 auto f = as_binding(false_value);
558 if (is<binding<double>>(t) && is<binding<double>>(f))
559 return ternary(cond, as<binding<double>>(t), as<binding<double>>(f));
561 else if (is<binding<std::wstring>>(t) && is<binding<std::wstring>>(f))
564 as<binding<std::wstring>>(t),
565 as<binding<std::wstring>>(f));
568 return ternary(cond, require<bool>(t), require<bool>(f));
571 void resolve_operators(int precedence, std::vector<boost::any>& tokens)
573 for (int i = 0; i < tokens.size(); ++i)
575 auto& token = tokens.at(i);
580 auto op_token = as<op>(token);
582 if (op_token.precedence != precedence)
585 int index_after = i + 1;
586 auto& token_after = tokens.at(index_after);
588 switch (op_token.type)
590 case op::op_type::UNARY:
591 if (op_token.characters == L"unary-")
593 tokens.at(i) = negative(token_after);
595 else if (op_token.characters == L"!")
597 tokens.at(i) = not_(token_after);
600 tokens.erase(tokens.begin() + index_after);
603 case op::op_type::BINARY:
605 auto& token_before = tokens.at(i - 1);
607 if (op_token.characters == L"*")
608 token_before = multiply(token_before, token_after);
609 else if (op_token.characters == L"/")
610 token_before = divide(token_before, token_after);
611 else if (op_token.characters == L"%")
612 token_before = modulus(token_before, token_after);
613 else if (op_token.characters == L"+")
614 token_before = add(token_before, token_after);
615 else if (op_token.characters == L"-")
616 token_before = subtract(token_before, token_after);
617 else if (op_token.characters == L"<")
618 token_before = less(token_before, token_after);
619 else if (op_token.characters == L"<=")
620 token_before = less_or_equal(token_before, token_after);
621 else if (op_token.characters == L">")
622 token_before = greater(token_before, token_after);
623 else if (op_token.characters == L">=")
624 token_before = greater_or_equal(token_before, token_after);
625 else if (op_token.characters == L"==")
626 token_before = equal(token_before, token_after);
627 else if (op_token.characters == L"!=")
628 token_before = not_(equal(token_before, token_after));
629 else if (op_token.characters == L"&&")
630 token_before = and_(token_before, token_after);
631 else if (op_token.characters == L"||")
632 token_before = or_(token_before, token_after);
635 tokens.erase(tokens.begin() + i, tokens.begin() + i + 2);
639 case op::op_type::TERNARY:
640 if (op_token.characters == L"?")
642 auto& token_before = tokens.at(i - 1);
643 auto& token_colon_operator = tokens.at(i + 2);
645 if (as<op>(token_colon_operator).characters != L":")
646 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
647 L"Expected : as part of ternary expression"));
649 auto& token_false_value = tokens.at(i + 3);
650 token_before = ternary(
651 token_before, token_after, token_false_value);
652 tokens.erase(tokens.begin() + i, tokens.begin() + i + 4);
661 boost::any parse_expression(
662 std::wstring::const_iterator& cursor,
663 const std::wstring& str,
664 const variable_repository& var_repo)
666 std::vector<boost::any> tokens;
669 while (cursor != str.end())
671 wchar_t ch = next_non_whitespace(cursor, str, L"Expected expression");
685 tokens.push_back(parse_constant(cursor, str));
700 tokens.push_back(parse_operator(cursor, str));
703 tokens.push_back(parse_string_literal(cursor, str));
706 tokens.push_back(parse_parenthesis(cursor, str, var_repo));
712 tokens.push_back(parse_variable(cursor, str, var_repo));
721 CASPAR_THROW_EXCEPTION(caspar_exception()
722 << msg_info(L"Expected expression"));
726 while (tokens.size() > 1)
728 resolve_operators(precedence++, tokens);
731 return as_binding(tokens.at(0));
735 binding<std::wstring> parse_expression(
736 const std::wstring& str, const variable_repository& var_repo)
738 auto cursor = str.cbegin();
739 auto expr = parse_expression(cursor, str, var_repo);
741 if (is<binding<std::wstring>>(expr))
742 return as<binding<std::wstring>>(expr);
743 else if (is<binding<double>>(expr))
744 return as<binding<double>>(expr).as<std::wstring>();
746 CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(
747 L"parse_expression() Unsupported type "
748 + u16(expr.type().name())));