1 /***************************************************************************
2 * Copyright (C) 2008-2016 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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. *
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. *
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 ***************************************************************************/
23 #include "format_impl.h"
24 #include "utility/type_conversions.h"
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
))
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
)
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
,
65 expressions
<CharT
> tmp
, result
;
66 auto push_token
= [&] {
68 result
.push_back(std::move(token
));
70 for (; it
!= end
; ++it
)
76 Format::FirstOf
<CharT
> first_of
;
81 // get to the corresponding closing bracket
82 unsigned brackets
= 1;
92 // check if we're still in range
93 rangeCheck(s
, jt
, end
);
94 // skip the opening bracket
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.
101 first_of
.base().push_back(std::move(tmp
[0]));
103 first_of
.base().push_back(Format::Group
<CharT
>(std::move(tmp
)));
105 // check for the alternative
107 if (jt
!= end
&& *jt
== '|')
110 rangeCheck(s
, jt
, end
);
112 throwError(s
, jt
, invalidCharacter(*jt
) + ", expected '{'");
118 assert(!first_of
.base().empty());
119 result
.push_back(std::move(first_of
));
121 else if (flags
& Format::Flags::Tag
&& *it
== '%')
124 rangeCheck(s
, it
, end
);
132 // check for tag delimiter
133 unsigned delimiter
= 0;
136 string
<CharT
> sdelimiter
;
139 while (it
!= end
&& isdigit(*it
));
140 rangeCheck(s
, it
, end
);
141 delimiter
= boost::lexical_cast
<unsigned>(sdelimiter
);
143 auto f
= charToGetFunction(*it
);
145 throwError(s
, it
, invalidCharacter(*it
));
146 result
.push_back(Format::SongTag(f
, delimiter
));
148 else if (flags
& properties
&& *it
== '$')
151 rangeCheck(s
, it
, end
);
160 if (flags
& Format::Flags::Color
&& isdigit(*it
))
162 auto color
= charToColor(*it
);
163 result
.push_back(color
);
166 else if (flags
& Format::Flags::Color
&& *it
== '(')
169 rangeCheck(s
, it
, end
);
171 string
<CharT
> scolor
;
174 while (it
!= end
&& *it
!= ')');
175 rangeCheck(s
, it
, end
);
176 auto value
= convertString
<char, CharT
>::apply(scolor
);
178 result
.push_back(boost::lexical_cast
<NC::Color
>(value
));
179 } catch (boost::bad_lexical_cast
&) {
180 throwError(s
, jt
, "invalid color \"" + value
+ "\"");
184 else if (flags
& Format::Flags::OutputSwitch
&& *it
== 'R')
185 result
.push_back(Format::OutputSwitch());
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
== '/')
198 rangeCheck(s
, it
, end
);
200 result
.push_back(NC::Format::NoBold
);
202 result
.push_back(NC::Format::NoUnderline
);
204 result
.push_back(NC::Format::NoAltCharset
);
206 result
.push_back(NC::Format::NoReverse
);
208 throwError(s
, it
, invalidCharacter(*it
));
211 throwError(s
, it
, invalidCharacter(*it
));
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
));