The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
string_utils.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 by David White <[email protected]>
3  Copyright (C) 2005 - 2016 by Guillaume Melquiond <[email protected]>
4  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
5 
6  This program 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 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "formula/string_utils.hpp"
17 
18 #include "config.hpp"
19 #include "log.hpp"
20 #include "formula/formula.hpp"
21 #include "gettext.hpp"
22 
23 static lg::log_domain log_engine("engine");
24 #define ERR_NG LOG_STREAM(err, log_engine)
25 
26 static bool two_dots(char a, char b) { return a == '.' && b == '.'; }
27 
28 namespace utils {
29 
31 {
32 public:
33  string_map_variable_set(const string_map& map) : map_(map) {}
34 
36  {
38  const string_map::const_iterator itor = map_.find(key);
39  if (itor != map_.end())
40  val = itor->second;
41  return val;
42  }
43 private:
44  const string_map& map_;
45 
46 };
47 }
48 
50 {
51  std::string res = str;
52  // This needs to be able to store negative numbers to check for the while's condition
53  // (which is only false when the previous '$' was at index 0)
54  int rfind_dollars_sign_from = res.size();
55  while(rfind_dollars_sign_from >= 0) {
56  // Going in a backwards order allows nested variable-retrieval, e.g. in arrays.
57  // For example, "I am $creatures[$i].user_description!"
58  const std::string::size_type var_begin_loc = res.rfind('$', rfind_dollars_sign_from);
59 
60  // If there are no '$' left then we're done.
61  if(var_begin_loc == std::string::npos) {
62  break;
63  }
64 
65  // For the next iteration of the loop, search for more '$'
66  // (not from the same place because sometimes the '$' is not replaced)
67  rfind_dollars_sign_from = int(var_begin_loc) - 1;
68 
69 
70  const std::string::iterator var_begin = res.begin() + var_begin_loc;
71 
72  // The '$' is not part of the variable name.
73  const std::string::iterator var_name_begin = var_begin + 1;
74  std::string::iterator var_end = var_name_begin;
75 
76  if(var_name_begin == res.end()) {
77  // Any '$' at the end of a string is just a '$'
78  continue;
79  } else if(*var_name_begin == '(') {
80  // The $( ... ) syntax invokes a formula
81  int paren_nesting_level = 0;
82  bool in_string = false,
83  in_comment = false;
84  do {
85  switch(*var_end) {
86  case '(':
87  if(!in_string && !in_comment) {
88  ++paren_nesting_level;
89  }
90  break;
91  case ')':
92  if(!in_string && !in_comment) {
93  --paren_nesting_level;
94  }
95  break;
96  case '#':
97  if(!in_string) {
98  in_comment = !in_comment;
99  }
100  break;
101  case '\'':
102  if(!in_comment) {
103  in_string = !in_string;
104  }
105  break;
106  // TODO: support escape sequences when/if they are allowed in FormulaAI strings
107  }
108  } while(++var_end != res.end() && paren_nesting_level > 0);
109  if(paren_nesting_level > 0) {
110  ERR_NG << "Formula in WML string cannot be evaluated due to "
111  << "a missing closing parenthesis:\n\t--> \""
112  << std::string(var_begin, var_end) << "\"\n";
113  res.replace(var_begin, var_end, "");
114  continue;
115  }
116  try {
117  const game_logic::formula form(std::string(var_begin+2, var_end-1));
118  res.replace(var_begin, var_end, form.evaluate().string_cast());
119  } catch(game_logic::formula_error& e) {
120  ERR_NG << "Formula in WML string cannot be evaluated due to "
121  << e.type << "\n\t--> \""
122  << e.formula << "\"\n";
123  res.replace(var_begin, var_end, "");
124  }
125  continue;
126  }
127 
128  // Find the maximum extent of the variable name (it may be shortened later).
129  for(int bracket_nesting_level = 0; var_end != res.end(); ++var_end) {
130  const char c = *var_end;
131  if(c == '[') {
132  ++bracket_nesting_level;
133  }
134  else if(c == ']') {
135  if(--bracket_nesting_level < 0) {
136  break;
137  }
138  }
139  // isascii() breaks on mingw with -std=c++0x
140  else if (!(((c) & ~0x7f) == 0)/*isascii(c)*/ || (!isalnum(c) && c != '.' && c != '_')) {
141  break;
142  }
143  }
144 
145  // Two dots in a row cannot be part of a valid variable name.
146  // That matters for random=, e.g. $x..$y
147  var_end = std::adjacent_find(var_name_begin, var_end, two_dots);
148  /// the default value is specified after ''?'
149  const std::string::iterator default_start = var_end < res.end() && *var_end == '?' ? var_end + 1 : res.end();
150 
151  // If the last character is '.', then it can't be a sub-variable.
152  // It's probably meant to be a period instead. Don't include it.
153  // Would need to do it repetitively if there are multiple '.'s at the end,
154  // but don't actually need to do so because the previous check for adjacent '.'s would catch that.
155  // For example, "My score is $score." or "My score is $score..."
156  if(*(var_end-1) == '.'
157  // However, "$array[$i]" by itself does not name a variable,
158  // so if "$array[$i]." is encountered, then best to include the '.',
159  // so that it more closely follows the syntax of a variable (if only to get rid of all of it).
160  // (If it's the script writer's error, they'll have to fix it in either case.)
161  // For example in "$array[$i].$field_name", if field_name does not exist as a variable,
162  // then the result of the expansion should be "", not "." (which it would be if this exception did not exist).
163  && *(var_end-2) != ']') {
164  --var_end;
165  }
166 
167  const std::string var_name(var_name_begin, var_end);
168  if(default_start == res.end()) {
169  if(var_end != res.end() && *var_end == '|') {
170  // It's been used to end this variable name; now it has no more effect.
171  // This can allow use of things like "$$composite_var_name|.x"
172  // (Yes, that's a WML 'pointer' of sorts. They are sometimes useful.)
173  // If there should still be a '|' there afterwards to affect other variable names (unlikely),
174  // just put another '|' there, one matching each '$', e.g. "$$var_containing_var_name||blah"
175  ++var_end;
176  }
177 
178 
179  if (var_name == "") {
180  // Allow for a way to have $s in a string.
181  // $| will be replaced by $.
182  res.replace(var_begin, var_end, "$");
183  }
184  else {
185  // The variable is replaced with its value.
186  res.replace(var_begin, var_end,
187  set.get_variable_const(var_name));
188  }
189  }
190  else {
191  var_end = default_start;
192  while(var_end != res.end() && *var_end != '|') {
193  ++var_end;
194  }
195  const std::string::iterator default_end = var_end;
196  const config::attribute_value& val = set.get_variable_const(var_name);
197  if(var_end == res.end()) {
198  res.replace(var_begin, default_start - 1, val);
199  }
200  else if(!val.blank()) {
201  res.replace(var_begin, var_end + 1, val);
202  }
203  else {
204  res.replace(var_begin, var_end + 1, std::string(default_start, default_end));
205  }
206  }
207  }
208 
209  return res;
210 }
211 
212 namespace utils {
213 
215 {
216  string_map_variable_set set(*symbols);
217  return do_interpolation(str, set);
218 }
219 
221 {
222  return do_interpolation(str, variables);
223 }
224 
226 {
227  if(!tstr.str().empty()) {
229  if(tstr.str() != interp) {
230  return t_string(interp);
231  }
232  }
233  return tstr;
234 }
235 
236 }
237 
238 std::string vgettext(const char *msgid, const utils::string_map& symbols)
239 {
240  const std::string orig(_(msgid));
242  return msg;
243 }
244 
245 std::string vgettext(const char *domain
246  , const char *msgid
247  , const utils::string_map& symbols)
248 {
249  const std::string orig(translation::dgettext(domain, msgid));
251  return msg;
252 }
253 std::string vngettext(const char* sing, const char* plur, int n, const utils::string_map& symbols)
254 {
255  const std::string orig(_n(sing, plur, n));
257  return msg;
258 }
#define ERR_NG
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:154
const GLfloat * c
Definition: glew.h:12741
std::string dgettext(const char *domain, const char *msgid)
Definition: gettext.cpp:39
GLuint const GLfloat * val
Definition: glew.h:2614
Definitions for the interface to Wesnoth Markup Language (WML).
Variant for storing WML attributes.
Definition: config.hpp:223
bool blank() const
Tests for an attribute that was never set.
Definition: config.cpp:367
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
std::map< std::string, t_string > string_map
static std::string do_interpolation(const std::string &str, const variable_set &set)
GLuint interp
Definition: glew.h:6176
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
static UNUSEDNOWARN std::string _n(const char *str1, const char *str2, int n)
Definition: gettext.hpp:86
std::string string_cast() const
Definition: variant.cpp:1165
string_map_variable_set(const string_map &map)
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
GLuint res
Definition: glew.h:9258
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:31
static bool two_dots(char a, char b)
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
static lg::log_domain log_engine("engine")
std::string vgettext(const char *msgid, const utils::string_map &symbols)
virtual config::attribute_value get_variable_const(const std::string &id) const =0
GLclampd n
Definition: glew.h:5903
Standard logging facilities (interface).
virtual config::attribute_value get_variable_const(const std::string &key) const
std::string vngettext(const char *sing, const char *plur, int n, const utils::string_map &symbols)
#define e
const std::string & str() const
Definition: tstring.hpp:170
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
t_string interpolate_variables_into_tstring(const t_string &tstr, const variable_set &variables)
Function that does the same as the above, for t_stringS.
GLsizei const GLcharARB ** string
Definition: glew.h:4503