29 std::string
make_link(
const std::string& text,
const std::string&
dst)
32 return formatter() <<
"<ref dst='" <<
dst <<
"'>" << text <<
"</ref>";
35 std::string
img(
const std::string&
src,
const std::string& align,
bool floating)
38 <<
"<img src='" <<
src <<
"' "
39 <<
"float='" << std::boolalpha << floating <<
"' "
40 <<
"align='" << align <<
"' "
97 static std::string
position_info(
const std::string::const_iterator& text_start,
const std::string::const_iterator& error_position)
100 int lines = std::count(text_start, error_position,
'\n') + 1;
103 auto pos = error_position;
104 for(; pos != text_start && *pos !=
'\n'; pos--);
107 <<
", character " <<
utf8::size(pos, error_position);
114 enum { UNKNOWN, NAMED, HEX, DECIMAL }
type = UNKNOWN;
117 for(; beg != end && *beg !=
';'; ++beg) {
122 }
else if(isalnum(*beg) || *beg ==
'_') {
126 throw parse_error(beg,
"invalid entity: unexpected characters after '&', alphanumeric characters, '#' or '_' expected.");
131 throw parse_error(beg,
"invalid entity: non-alphanumeric characters after '&'.");
138 }
else if(isdigit(*beg)) {
141 throw parse_error(beg,
"invalid entity: unexpected characters after '&#', numbers or 'x' expected.");
148 throw parse_error(beg,
"invalid entity: unexpected characters after '&#x', hexadecimal digits expected.");
154 std::string name =
s.str();
155 entity[
"name"] = name;
157 entity[
"code_point"] =
'<';
158 }
else if(name ==
"gt") {
159 entity[
"code_point"] =
'>';
160 }
else if(name ==
"apos") {
161 entity[
"code_point"] =
'\'';
162 }
else if(name ==
"quot") {
163 entity[
"code_point"] =
'"';
164 }
else if(name ==
"amp") {
165 entity[
"code_point"] =
'&';
174 entity[
"code_point"] =
n;
179 static char parse_escape(std::string::const_iterator& beg, std::string::const_iterator end)
181 assert(*beg ==
'\\');
184 if((beg + 1) != end) {
195 std::ostringstream
s;
196 bool saw_newline =
false;
198 for(; beg != end && *beg != close; ++beg) {
202 throw parse_error(beg,
"unexpected end of stream after entity");
204 if(entity.has_attribute(
"code_point")) {
205 s << unicode_cast<std::string>(entity[
"code_point"].to_int());
209 res.
add_child(
"character_entity", entity);
212 }
else if(*beg ==
'\\') {
214 }
else if(*beg ==
'\n') {
235 assert(beg == end || *beg == close);
239 static std::string
parse_name(std::string::const_iterator& beg, std::string::const_iterator end)
241 std::ostringstream
s;
242 for(; beg != end && (isalnum(*beg) || *beg ==
'_'); ++beg) {
248 static std::pair<std::string, std::string>
parse_attribute(std::string::const_iterator& beg, std::string::const_iterator end,
bool allow_empty)
254 while(isspace(*beg)) ++beg;
262 }
else throw parse_error(beg,
"attribute missing value in old-style tag");
265 while(isspace(*beg)) ++beg;
268 if(*beg ==
'\'' || *beg ==
'"') {
271 throw parse_error(beg,
"unsupported entity in attribute value");
273 throw parse_error(beg,
"paragraph break in attribute value");
276 value =
t[
"text"].str();
279 std::ostringstream
s;
280 bool found_slash =
false;
281 for(; beg != end && *beg !=
'>' && *beg !=
'<' && !isspace(*beg); ++beg) {
285 throw parse_error(beg,
"unexpected end of stream after entity");
287 if(entity.has_attribute(
"code_point")) {
288 s << unicode_cast<std::string>(entity[
"code_point"].to_int());
290 throw parse_error(beg,
"unsupported entity in attribute value");
292 }
else if(*beg ==
'\\') {
294 }
else if(*beg ==
'/') {
308 if(found_slash) --beg;
310 return {attr, value};
313 static void check_closing_tag(std::string::const_iterator& beg, std::string::const_iterator end, std::string_view match)
315 std::size_t remaining = end - beg;
316 assert(remaining >= 2 && *beg ==
'<' && *(beg + 1) ==
'/');
317 if(remaining < match.size() + 3) {
318 throw parse_error(beg,
"Unexpected end of stream in closing tag");
321 if(!std::equal(match.begin(), match.end(), beg)) {
322 throw parse_error(beg,
"Mismatched closing tag " + std::string(match));
326 throw parse_error(beg,
"Unterminated closing tag " + std::string(match));
331 static std::pair<std::string, config>
parse_tag(std::string::const_iterator& beg, std::string::const_iterator end);
332 static config parse_tag_contents(std::string::const_iterator& beg, std::string::const_iterator end, std::string_view match,
bool check_for_attributes)
338 for(; check_for_attributes && beg != end && *beg !=
'<'; ++beg) {
339 if(isspace(*beg))
continue;
346 while(beg != end && isspace(*beg)) ++beg;
351 if(beg == end || *beg !=
'<' || (beg + 1) == end || *(beg + 1) !=
'/') {
352 throw parse_error(beg,
"Extra text at the end of old-style tag with explicit 'text' attribute");
358 if(beg == end || *beg !=
'<' || (beg + 1) == end || *(beg + 1) !=
'/') {
359 throw parse_error(beg,
"Extra text at the end of old-style tag with explicit 'text' attribute");
371 if(beg == end || beg + 1 == end) {
372 throw parse_error(beg,
"Missing closing tag for " + std::string(match));
375 if(*(beg + 1) ==
'/') {
388 static std::pair<std::string, config>
parse_tag(std::string::const_iterator& beg, std::string::const_iterator end)
393 if(tag_name.empty()) {
396 bool auto_closed =
false;
398 for(; beg != end && *beg !=
'>'; ++beg) {
399 if(isspace(*beg))
continue;
400 if(*beg ==
'/' && (beg + 1) != end && *(beg + 1) ==
'>') {
402 }
else if(isalnum(*beg) || *beg ==
'_') {
405 throw parse_error(beg,
"unexpected end of stream following attribute");
416 elem[
"text"] = contents[
"text"];
421 return {tag_name, elem};
427 auto beg = text.begin(), end = text.end();
442 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)
static config parse_tag_contents(std::string::const_iterator &beg, std::string::const_iterator end, std::string_view match, bool check_for_attributes)
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 std::pair< std::string, std::string > parse_attribute(std::string::const_iterator &beg, std::string::const_iterator end, bool allow_empty)
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 void check_closing_tag(std::string::const_iterator &beg, std::string::const_iterator end, std::string_view match)
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.
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