Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "formula_string_utils.hpp"
00018
00019 #include "config.hpp"
00020 #include "log.hpp"
00021 #include "formula.hpp"
00022 #include "gettext.hpp"
00023
00024 static lg::log_domain log_engine("engine");
00025 #define ERR_NG LOG_STREAM(err, log_engine)
00026
00027 static bool two_dots(char a, char b) { return a == '.' && b == '.'; }
00028
00029 namespace utils {
00030
00031 class string_map_variable_set : public variable_set
00032 {
00033 public:
00034 string_map_variable_set(const string_map& map) : map_(map) {};
00035
00036 virtual config::attribute_value get_variable_const(const std::string &key) const
00037 {
00038 config::attribute_value val;
00039 const string_map::const_iterator itor = map_.find(key);
00040 if (itor != map_.end())
00041 val = itor->second;
00042 return val;
00043 }
00044 private:
00045 const string_map& map_;
00046
00047 };
00048 }
00049
00050 static std::string do_interpolation(const std::string &str, const variable_set& set)
00051 {
00052 std::string res = str;
00053
00054
00055 int rfind_dollars_sign_from = res.size();
00056 while(rfind_dollars_sign_from >= 0) {
00057
00058
00059 const std::string::size_type var_begin_loc = res.rfind('$', rfind_dollars_sign_from);
00060
00061
00062 if(var_begin_loc == std::string::npos) {
00063 break;
00064 }
00065
00066
00067
00068 rfind_dollars_sign_from = int(var_begin_loc) - 1;
00069
00070
00071 const std::string::iterator var_begin = res.begin() + var_begin_loc;
00072
00073
00074 const std::string::iterator var_name_begin = var_begin + 1;
00075 std::string::iterator var_end = var_name_begin;
00076
00077 if(var_name_begin == res.end()) {
00078
00079 continue;
00080 } else if(*var_name_begin == '(') {
00081
00082 int paren_nesting_level = 0;
00083 bool in_string = false,
00084 in_comment = false;
00085 do {
00086 switch(*var_end) {
00087 case '(':
00088 if(!in_string && !in_comment) {
00089 ++paren_nesting_level;
00090 }
00091 break;
00092 case ')':
00093 if(!in_string && !in_comment) {
00094 --paren_nesting_level;
00095 }
00096 break;
00097 case '#':
00098 if(!in_string) {
00099 in_comment = !in_comment;
00100 }
00101 break;
00102 case '\'':
00103 if(!in_comment) {
00104 in_string = !in_string;
00105 }
00106 break;
00107
00108 }
00109 } while(++var_end != res.end() && paren_nesting_level > 0);
00110 if(paren_nesting_level > 0) {
00111 ERR_NG << "Formula in WML string cannot be evaluated due to "
00112 << "a missing closing parenthesis:\n\t--> \""
00113 << std::string(var_begin, var_end) << "\"\n";
00114 res.replace(var_begin, var_end, "");
00115 continue;
00116 }
00117 try {
00118 const game_logic::formula form(std::string(var_begin+2, var_end-1));
00119 res.replace(var_begin, var_end, form.evaluate().string_cast());
00120 } catch(game_logic::formula_error& e) {
00121 ERR_NG << "Formula in WML string cannot be evaluated due to "
00122 << e.type << "\n\t--> \""
00123 << e.formula << "\"\n";
00124 res.replace(var_begin, var_end, "");
00125 }
00126 continue;
00127 }
00128
00129
00130 for(int bracket_nesting_level = 0; var_end != res.end(); ++var_end) {
00131 const char c = *var_end;
00132 if(c == '[') {
00133 ++bracket_nesting_level;
00134 }
00135 else if(c == ']') {
00136 if(--bracket_nesting_level < 0) {
00137 break;
00138 }
00139 }
00140
00141 else if (!(((c) & ~0x7f) == 0) || (!isalnum(c) && c != '.' && c != '_')) {
00142 break;
00143 }
00144 }
00145
00146
00147
00148 var_end = std::adjacent_find(var_name_begin, var_end, two_dots);
00149
00150
00151
00152
00153
00154
00155 if(*(var_end-1) == '.'
00156
00157
00158
00159
00160
00161
00162 && *(var_end-2) != ']') {
00163 --var_end;
00164 }
00165
00166 const std::string var_name(var_name_begin, var_end);
00167
00168 if(var_end != res.end() && *var_end == '|') {
00169
00170
00171
00172
00173
00174 ++var_end;
00175 }
00176
00177
00178 if (var_name == "") {
00179
00180
00181 res.replace(var_begin, var_end, "$");
00182 }
00183 else {
00184
00185 res.replace(var_begin, var_end,
00186 set.get_variable_const(var_name));
00187 }
00188 }
00189
00190 return res;
00191 }
00192
00193 namespace utils {
00194
00195 std::string interpolate_variables_into_string(const std::string &str, const string_map * const symbols)
00196 {
00197 string_map_variable_set set(*symbols);
00198 return do_interpolation(str, set);
00199 }
00200
00201 std::string interpolate_variables_into_string(const std::string &str, const variable_set& variables)
00202 {
00203 return do_interpolation(str, variables);
00204 }
00205
00206 t_string interpolate_variables_into_tstring(const t_string &tstr, const variable_set& variables)
00207 {
00208 if(!tstr.str().empty()) {
00209 std::string interp = utils::interpolate_variables_into_string(tstr.str(), variables);
00210 if(tstr.str() != interp) {
00211 return t_string(interp);
00212 }
00213 }
00214 return tstr;
00215 }
00216
00217 }
00218
00219 std::string vgettext(const char *msgid, const utils::string_map& symbols)
00220 {
00221 const std::string orig(_(msgid));
00222 const std::string msg = utils::interpolate_variables_into_string(orig, &symbols);
00223 return msg;
00224 }
00225
00226 std::string vgettext(const char *domain
00227 , const char *msgid
00228 , const utils::string_map& symbols)
00229 {
00230 const std::string orig(dgettext(domain, msgid));
00231 const std::string msg = utils::interpolate_variables_into_string(orig, &symbols);
00232 return msg;
00233 }
00234 std::string vngettext(const char* sing, const char* plur, int n, const utils::string_map& symbols)
00235 {
00236 const std::string orig(_n(sing, plur, n));
00237 const std::string msg = utils::interpolate_variables_into_string(orig, &symbols);
00238 return msg;
00239 }