actions: use unique_ptr for storing actions
[ncmpcpp.git] / src / format.cpp
blobcc655908107cc8d07ff022c442bdcf853d3e4e33
1 /***************************************************************************
2 * Copyright (C) 2008-2016 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <stdexcept>
23 #include "format_impl.h"
24 #include "utility/type_conversions.h"
26 namespace {
28 const unsigned properties = Format::Flags::Color
29 | Format::Flags::Format
30 | Format::Flags::OutputSwitch;
32 template <typename CharT> using string = std::basic_string<CharT>;
33 template <typename CharT> using iterator = typename std::basic_string<CharT>::const_iterator;
34 template <typename CharT> using expressions = std::vector<Format::Expression<CharT>>;
36 template <typename CharT>
37 std::string invalidCharacter(CharT c)
39 return "invalid character '"
40 + convertString<char, CharT>::apply(boost::lexical_cast<string<CharT>>(c))
41 + "'";
44 template <typename CharT>
45 void throwError(const string<CharT> &s, iterator<CharT> current, std::string msg)
47 throw std::runtime_error(
48 std::move(msg) + " at position " + boost::lexical_cast<std::string>(current - s.begin())
52 template <typename CharT>
53 void rangeCheck(const string<CharT> &s, iterator<CharT> current, iterator<CharT> end)
55 if (current >= end)
56 throwError(s, current, "unexpected end");
59 template <typename CharT>
60 expressions<CharT> parseBracket(const string<CharT> &s,
61 iterator<CharT> it, iterator<CharT> end,
62 const unsigned flags)
64 string<CharT> token;
65 expressions<CharT> tmp, result;
66 auto push_token = [&] {
67 if (!token.empty())
68 result.push_back(std::move(token));
70 for (; it != end; ++it)
72 if (*it == '{')
74 push_token();
75 bool done;
76 Format::FirstOf<CharT> first_of;
79 auto jt = it;
80 done = true;
81 // get to the corresponding closing bracket
82 unsigned brackets = 1;
83 while (brackets > 0)
85 if (++jt == end)
86 break;
87 if (*jt == '{')
88 ++brackets;
89 else if (*jt == '}')
90 --brackets;
92 // check if we're still in range
93 rangeCheck(s, jt, end);
94 // skip the opening bracket
95 ++it;
96 // recursively parse the bracket
97 tmp = parseBracket(s, it, jt, flags);
98 // if the inner bracket contains only one expression,
99 // put it as is. otherwise make a group out of them.
100 if (tmp.size() == 1)
101 first_of.base().push_back(std::move(tmp[0]));
102 else
103 first_of.base().push_back(Format::Group<CharT>(std::move(tmp)));
104 it = jt;
105 // check for the alternative
106 ++jt;
107 if (jt != end && *jt == '|')
109 ++jt;
110 rangeCheck(s, jt, end);
111 if (*jt != '{')
112 throwError(s, jt, invalidCharacter(*jt) + ", expected '{'");
113 it = jt;
114 done = false;
117 while (!done);
118 assert(!first_of.base().empty());
119 result.push_back(std::move(first_of));
121 else if (flags & Format::Flags::Tag && *it == '%')
123 ++it;
124 rangeCheck(s, it, end);
125 // %% is escaped %
126 if (*it == '%')
128 token += '%';
129 continue;
131 push_token();
132 // check for tag delimiter
133 unsigned delimiter = 0;
134 if (isdigit(*it))
136 string<CharT> sdelimiter;
138 sdelimiter += *it++;
139 while (it != end && isdigit(*it));
140 rangeCheck(s, it, end);
141 delimiter = boost::lexical_cast<unsigned>(sdelimiter);
143 auto f = charToGetFunction(*it);
144 if (f == nullptr)
145 throwError(s, it, invalidCharacter(*it));
146 result.push_back(Format::SongTag(f, delimiter));
148 else if (flags & properties && *it == '$')
150 ++it;
151 rangeCheck(s, it, end);
152 // $$ is escaped $
153 if (*it == '$')
155 token += '$';
156 continue;
158 push_token();
159 // legacy colors
160 if (flags & Format::Flags::Color && isdigit(*it))
162 auto color = charToColor(*it);
163 result.push_back(color);
165 // new colors
166 else if (flags & Format::Flags::Color && *it == '(')
168 ++it;
169 rangeCheck(s, it, end);
170 auto jt = it;
171 string<CharT> scolor;
173 scolor += *it++;
174 while (it != end && *it != ')');
175 rangeCheck(s, it, end);
176 auto value = convertString<char, CharT>::apply(scolor);
177 try {
178 result.push_back(boost::lexical_cast<NC::Color>(value));
179 } catch (boost::bad_lexical_cast &) {
180 throwError(s, jt, "invalid color \"" + value + "\"");
183 // output switch
184 else if (flags & Format::Flags::OutputSwitch && *it == 'R')
185 result.push_back(Format::OutputSwitch());
186 // format
187 else if (flags & Format::Flags::Format && *it == 'b')
188 result.push_back(NC::Format::Bold);
189 else if (flags & Format::Flags::Format && *it == 'u')
190 result.push_back(NC::Format::Underline);
191 else if (flags & Format::Flags::Format && *it == 'a')
192 result.push_back(NC::Format::AltCharset);
193 else if (flags & Format::Flags::Format && *it == 'r')
194 result.push_back(NC::Format::Reverse);
195 else if (flags & Format::Flags::Format && *it == '/')
197 ++it;
198 rangeCheck(s, it, end);
199 if (*it == 'b')
200 result.push_back(NC::Format::NoBold);
201 else if (*it == 'u')
202 result.push_back(NC::Format::NoUnderline);
203 else if (*it == 'a')
204 result.push_back(NC::Format::NoAltCharset);
205 else if (*it == 'r')
206 result.push_back(NC::Format::NoReverse);
207 else
208 throwError(s, it, invalidCharacter(*it));
210 else
211 throwError(s, it, invalidCharacter(*it));
213 else
214 token += *it;
216 push_token();
217 return result;
222 namespace Format {
224 AST<char> parse(const std::string &s, const unsigned flags)
226 return AST<char>(parseBracket(s, s.begin(), s.end(), flags));
229 AST<wchar_t> parse(const std::wstring &s, const unsigned flags)
231 return AST<wchar_t>(parseBracket(s, s.begin(), s.end(), flags));