30 std::string
make_link(
const std::string& text,
const std::string&
dst)
33 return formatter() <<
"<ref dst='" <<
dst <<
"'>" << text <<
"</ref>";
36 std::string
img(
const std::string&
src,
const std::string& align,
bool floating)
39 <<
"<img src='" <<
src <<
"' "
40 <<
"float='" << std::boolalpha << floating <<
"' "
41 <<
"align='" << align <<
"' "
100 using namespace std::string_literals;
101 const std::array old_style_tags{
"bold"s,
"italic"s,
"header"s,
"format"s,
"img"s,
"ref"s,
"jump"s };
102 const std::array old_style_attr{
"dst"s,
"text"s,
"force"s,
"to"s,
"amount"s,
"src"s,
"align"s,
"float"s,
"bold"s,
"italic"s,
"color"s,
"font_size"s };
105 static std::string
position_info(
const std::string::const_iterator& text_start,
const std::string::const_iterator& error_position)
108 int lines = std::count(text_start, error_position,
'\n') + 1;
111 auto pos = error_position;
112 for(; pos != text_start && *pos !=
'\n'; pos--);
115 <<
", character " <<
utf8::size(pos, error_position);
122 enum { UNKNOWN, NAMED, HEX, DECIMAL }
type = UNKNOWN;
125 for(; beg != end && *beg !=
';'; ++beg) {
130 }
else if(isalnum(*beg) || *beg ==
'_') {
134 throw parse_error(beg,
"invalid entity: unexpected characters after '&', alphanumeric characters, '#' or '_' expected.");
139 throw parse_error(beg,
"invalid entity: non-alphanumeric characters after '&'.");
146 }
else if(isdigit(*beg)) {
149 throw parse_error(beg,
"invalid entity: unexpected characters after '&#', numbers or 'x' expected.");
156 throw parse_error(beg,
"invalid entity: unexpected characters after '&#x', hexadecimal digits expected.");
162 std::string name =
s.str();
163 entity[
"name"] = name;
165 entity[
"code_point"] =
'<';
166 }
else if(name ==
"gt") {
167 entity[
"code_point"] =
'>';
168 }
else if(name ==
"apos") {
169 entity[
"code_point"] =
'\'';
170 }
else if(name ==
"quot") {
171 entity[
"code_point"] =
'"';
172 }
else if(name ==
"amp") {
173 entity[
"code_point"] =
'&';
182 entity[
"code_point"] =
n;
187 static char parse_escape(std::string::const_iterator& beg, std::string::const_iterator end)
189 assert(*beg ==
'\\');
192 if((beg + 1) != end) {
203 std::ostringstream
s;
204 bool saw_newline =
false;
206 for(; beg != end && *beg != close; ++beg) {
210 throw parse_error(beg,
"unexpected end of stream after entity");
212 if(entity.has_attribute(
"code_point")) {
213 s << unicode_cast<std::string>(entity[
"code_point"].to_int());
217 res.
add_child(
"character_entity", entity);
220 }
else if(*beg ==
'\\') {
222 }
else if(*beg ==
'\n') {
243 assert(beg == end || *beg == close);
247 static std::string
parse_name(std::string::const_iterator& beg, std::string::const_iterator end)
249 std::ostringstream
s;
250 for(; beg != end && (isalnum(*beg) || *beg ==
'_'); ++beg) {
256 static std::pair<std::string, std::string>
parse_attribute(std::string::const_iterator& beg, std::string::const_iterator end,
bool old_style)
263 throw parse_error(beg,
"dummy error: not an old-style attribute name");
266 while(isspace(*beg)) ++beg;
270 throw parse_error(beg,
"attribute missing value in old-style tag");
279 while(isspace(*beg)) ++beg;
282 if(*beg ==
'\'' || *beg ==
'"') {
285 throw parse_error(beg,
"unsupported entity in attribute value");
287 throw parse_error(beg,
"paragraph break in attribute value");
290 value =
t[
"text"].str();
293 std::ostringstream
s;
294 bool found_slash =
false;
295 for(; beg != end && *beg !=
'>' && *beg !=
'<' && !isspace(*beg); ++beg) {
299 throw parse_error(beg,
"unexpected end of stream after entity");
301 if(entity.has_attribute(
"code_point")) {
302 s << unicode_cast<std::string>(entity[
"code_point"].to_int());
304 throw parse_error(beg,
"unsupported entity in attribute value");
306 }
else if(*beg ==
'\\') {
308 }
else if(*beg ==
'/') {
322 if(found_slash) --beg;
324 return {attr, value};
327 static void check_closing_tag(std::string::const_iterator& beg, std::string::const_iterator end, std::string_view tag_name)
329 std::size_t remaining = end - beg;
330 assert(remaining >= 2 && *beg ==
'<' && *(beg + 1) ==
'/');
331 if(remaining < tag_name.size() + 3) {
332 throw parse_error(beg,
"Unexpected end of stream in closing tag");
335 if(!std::equal(tag_name.begin(), tag_name.end(), beg)) {
336 throw parse_error(beg,
"Mismatched closing tag " + std::string(tag_name));
338 beg += tag_name.size();
340 throw parse_error(beg,
"Unterminated closing tag " + std::string(tag_name));
345 static std::pair<std::string, config>
parse_tag(std::string::const_iterator& beg, std::string::const_iterator end);
346 static config parse_tag_contents(std::string::const_iterator& beg, std::string::const_iterator end, std::string_view tag_name,
bool check_for_attributes)
352 check_for_attributes =
false;
357 for(; check_for_attributes && beg != end && *beg !=
'<'; ++beg) {
358 if(isspace(*beg))
continue;
365 while(beg != end && isspace(*beg)) ++beg;
370 if(beg == end || *beg !=
'<' || (beg + 1) == end || *(beg + 1) !=
'/') {
371 throw parse_error(beg,
"Extra text at the end of old-style tag with explicit 'text' attribute");
377 if(beg == end || *beg !=
'<' || (beg + 1) == end || *(beg + 1) !=
'/') {
378 throw parse_error(beg,
"Extra text at the end of old-style tag with explicit 'text' attribute");
390 if(beg == end || beg + 1 == end) {
391 throw parse_error(beg,
"Missing closing tag for " + std::string(tag_name));
394 if(*(beg + 1) ==
'/') {
407 static std::pair<std::string, config>
parse_tag(std::string::const_iterator& beg, std::string::const_iterator end)
412 if(tag_name.empty()) {
415 bool auto_closed =
false;
417 for(; beg != end && *beg !=
'>'; ++beg) {
418 if(isspace(*beg))
continue;
419 if(*beg ==
'/' && (beg + 1) != end && *(beg + 1) ==
'>') {
421 }
else if(isalnum(*beg) || *beg ==
'_') {
424 throw parse_error(beg,
"unexpected end of stream following attribute");
435 elem[
"text"] = contents[
"text"];
440 return {tag_name, elem};
446 auto beg = text.begin(), end = text.end();
461 e.message =
position_info(text.begin(),
e.error_location()) +
": " +
e.message;
A config object defines a single node in a WML file, with access to child nodes.
config & add_child(std::string_view key)
void append(const config &cfg)
Append data from another config object to this one.
std::size_t attribute_count() const
Count the number of non-blank attributes.
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
std::size_t all_children_count() const
bool has_attribute(std::string_view key) const
bool has_child(std::string_view key) const
Determine whether a config has a child or not.
void append_children(const config &cfg)
Adds children from cfg.
config & mandatory_child(std::string_view key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definitions for the interface to Wesnoth Markup Language (WML).
static std::string parse_name(std::string::const_iterator &beg, std::string::const_iterator end)
std::string img(const std::string &src, const std::string &align, bool floating)
Generates a Help markup tag corresponding to an image.
std::string make_link(const std::string &text, const std::string &dst)
Generates a Help markup tag corresponding to a reference or link.
static std::string position_info(const std::string::const_iterator &text_start, const std::string::const_iterator &error_position)
static config parse_tag_contents(std::string::const_iterator &beg, std::string::const_iterator end, std::string_view tag_name, bool check_for_attributes)
static void check_closing_tag(std::string::const_iterator &beg, std::string::const_iterator end, std::string_view tag_name)
static std::pair< std::string, config > parse_tag(std::string::const_iterator &beg, std::string::const_iterator end)
static char parse_escape(std::string::const_iterator &beg, std::string::const_iterator end)
static config parse_text_until(std::string::const_iterator &beg, std::string::const_iterator end, char close)
std::string tag(std::string_view tag, Args &&... data)
Wraps the given data in the specified tag.
static config parse_entity(std::string::const_iterator &beg, std::string::const_iterator end)
static std::pair< std::string, std::string > parse_attribute(std::string::const_iterator &beg, std::string::const_iterator end, bool old_style)
config parse_text(const std::string &text)
Parse a xml style marked up text string.
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
Thrown when the help system fails to parse something.
static map_location::direction n
static map_location::direction s