formula_function.cpp

Go to the documentation of this file.
00001 /* $Id: formula_function.cpp 25895 2008-04-17 18:57:13Z mordante $ */
00002 /*
00003    Copyright (C) 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 #include "global.hpp"
00016 
00017 #include <iostream>
00018 #include <math.h>
00019 
00020 //#include "foreach.hpp"
00021 #include "callable_objects.hpp"
00022 #include "formula_callable.hpp"
00023 #include "formula_function.hpp"
00024 #include "map.hpp"
00025 
00026 #include "SDL.h"
00027 
00028 namespace game_logic {
00029 
00030 namespace {
00031 
00032 class dir_function : public function_expression {
00033 public:
00034     explicit dir_function(const args_list& args)
00035          : function_expression("dir", args, 1, 1)
00036     {}
00037 
00038 private:
00039     variant execute(const formula_callable& variables) const {
00040         variant var = args()[0]->evaluate(variables);
00041         const formula_callable* callable = var.as_callable();
00042         std::vector<formula_input> inputs = callable->inputs();
00043         std::vector<variant> res;
00044         for(size_t i=0; i<inputs.size(); ++i) {
00045             const formula_input& input = inputs[i];
00046             res.push_back(variant(input.name));
00047         }
00048 
00049         return variant(&res);
00050     }
00051 };
00052 
00053 class if_function : public function_expression {
00054 public:
00055     explicit if_function(const args_list& args)
00056          : function_expression("if", args, 3, 3)
00057     {}
00058 
00059 private:
00060     variant execute(const formula_callable& variables) const {
00061         const int i = args()[0]->evaluate(variables).as_bool() ? 1 : 2;
00062         return args()[i]->evaluate(variables);
00063     }
00064 };
00065 
00066 class switch_function : public function_expression {
00067 public:
00068     explicit switch_function(const args_list& args)
00069         : function_expression("switch", args, 3, -1)
00070     {}
00071 
00072 private:
00073     variant execute(const formula_callable& variables) const {
00074         variant var = args()[0]->evaluate(variables);
00075         for(size_t n = 1; n < args().size()-1; n += 2) {
00076             variant val = args()[n]->evaluate(variables);
00077             if(val == var) {
00078                 return args()[n+1]->evaluate(variables);
00079             }
00080         }
00081 
00082         if((args().size()%2) == 0) {
00083             return args().back()->evaluate(variables);
00084         } else {
00085             return variant();
00086         }
00087     }
00088 };
00089 
00090 class rgb_function : public function_expression {
00091 public:
00092     explicit rgb_function(const args_list& args)
00093          : function_expression("rgb", args, 3, 3)
00094     {}
00095 
00096 private:
00097     variant execute(const formula_callable& variables) const {
00098         return variant(10000*
00099          std::min<int>(99,std::max<int>(0,args()[0]->evaluate(variables).as_int())) +
00100          std::min<int>(99,std::max<int>(0,args()[1]->evaluate(variables).as_int()))*100+
00101          std::min<int>(99,std::max<int>(0,args()[2]->evaluate(variables).as_int())));
00102     }
00103 };
00104 
00105 namespace {
00106 int transition(int begin, int val1, int end, int val2, int value) {
00107     if(value < begin || value > end) {
00108         return 0;
00109     }
00110 
00111     if(value == begin) {
00112         return val1;
00113     } else if(value == end) {
00114         return val2;
00115     }
00116 
00117     const int comp1 = val1*(end - value);
00118     const int comp2 = val2*(value - begin);
00119     return (comp1 + comp2)/(end - begin);
00120 }
00121 }
00122 
00123 class transition_function : public function_expression {
00124 public:
00125     explicit transition_function(const args_list& args)
00126             : function_expression("transition", args, 5, 5)
00127     {}
00128 private:
00129     variant execute(const formula_callable& variables) const {
00130         const int value = args()[0]->evaluate(variables).as_int();
00131         const int begin = args()[1]->evaluate(variables).as_int();
00132         const int end = args()[3]->evaluate(variables).as_int();
00133         if(value < begin || value > end) {
00134             return variant(0);
00135         }
00136         const int val1 = args()[2]->evaluate(variables).as_int();
00137         const int val2 = args()[4]->evaluate(variables).as_int();
00138         return variant(transition(begin, val1, end, val2, value));
00139     }
00140 };
00141 
00142 class color_transition_function : public function_expression {
00143 public:
00144     explicit color_transition_function(const args_list& args)
00145             : function_expression("color_transition", args, 5)
00146     {}
00147 private:
00148     variant execute(const formula_callable& variables) const {
00149         const int value = args()[0]->evaluate(variables).as_int();
00150         int begin = args()[1]->evaluate(variables).as_int();
00151         int end = -1;
00152         size_t n = 3;
00153         while(n < args().size()) {
00154             end = args()[n]->evaluate(variables).as_int();
00155             if(value >= begin && value <= end) {
00156                 break;
00157             }
00158 
00159             begin = end;
00160             n += 2;
00161         }
00162 
00163         if(value < begin || value > end) {
00164             return variant(0);
00165         }
00166         const int val1 = args()[n-1]->evaluate(variables).as_int();
00167         const int val2 = args()[n+1 < args().size() ? n+1 : n]->
00168                                        evaluate(variables).as_int();
00169         const int r1 = (val1/10000)%100;
00170         const int g1 = (val1/100)%100;
00171         const int b1 = (val1)%100;
00172         const int r2 = (val2/10000)%100;
00173         const int g2 = (val2/100)%100;
00174         const int b2 = (val2)%100;
00175 
00176         const int r = transition(begin,r1,end,r2,value);
00177         const int g = transition(begin,g1,end,g2,value);
00178         const int b = transition(begin,b1,end,b2,value);
00179         return variant(
00180                std::min<int>(99,std::max<int>(0,r))*100*100 +
00181                std::min<int>(99,std::max<int>(0,g))*100+
00182                std::min<int>(99,std::max<int>(0,b)));
00183     }
00184 };
00185 
00186 
00187 class abs_function : public function_expression {
00188 public:
00189     explicit abs_function(const args_list& args)
00190          : function_expression("abs", args, 1, 1)
00191     {}
00192 
00193 private:
00194     variant execute(const formula_callable& variables) const {
00195         const int n = args()[0]->evaluate(variables).as_int();
00196         return variant(n >= 0 ? n : -n);
00197     }
00198 };
00199 
00200 class min_function : public function_expression {
00201 public:
00202     explicit min_function(const args_list& args)
00203          : function_expression("min", args, 1, -1)
00204     {}
00205 
00206 private:
00207     variant execute(const formula_callable& variables) const {
00208         bool found = false;
00209         int res = 0;
00210         for(size_t n = 0; n != args().size(); ++n) {
00211             const variant v = args()[n]->evaluate(variables);
00212             if(v.is_list()) {
00213                 for(size_t m = 0; m != v.num_elements(); ++m) {
00214                     if(!found || v[m].as_int() < res) {
00215                         res = v[m].as_int();
00216                         found = true;
00217                     }
00218                 }
00219             } else if(v.is_int()) {
00220                 if(!found || v.as_int() < res) {
00221                     res = v.as_int();
00222                     found = true;
00223                 }
00224             }
00225         }
00226 
00227         return variant(res);
00228     }
00229 };
00230 
00231 class max_function : public function_expression {
00232 public:
00233     explicit max_function(const args_list& args)
00234          : function_expression("max", args, 1, -1)
00235     {}
00236 
00237 private:
00238     variant execute(const formula_callable& variables) const {
00239         bool found = false;
00240         int res = 0;
00241         for(size_t n = 0; n != args().size(); ++n) {
00242             const variant v = args()[n]->evaluate(variables);
00243             if(v.is_list()) {
00244                 for(size_t m = 0; m != v.num_elements(); ++m) {
00245                     if(!found || v[m].as_int() > res) {
00246                         res = v[m].as_int();
00247                         found = true;
00248                     }
00249                 }
00250             } else if(v.is_int()) {
00251                 if(!found || v.as_int() > res) {
00252                     res = v.as_int();
00253                     found = true;
00254                 }
00255             }
00256         }
00257 
00258         return variant(res);
00259     }
00260 };
00261 
00262 class keys_function : public function_expression {
00263 public:
00264     explicit keys_function(const args_list& args)
00265          : function_expression("keys", args, 1, 1)
00266     {}
00267 
00268 private:
00269     variant execute(const formula_callable& variables) const {
00270         const variant map = args()[0]->evaluate(variables);
00271         return map.get_keys();
00272     }
00273 };
00274 
00275 class values_function : public function_expression {
00276 public:
00277     explicit values_function(const args_list& args)
00278          : function_expression("values", args, 1, 1)
00279     {}
00280 
00281 private:
00282     variant execute(const formula_callable& variables) const {
00283         const variant map = args()[0]->evaluate(variables);
00284         return map.get_values();
00285     }
00286 };
00287 
00288 class choose_function : public function_expression {
00289 public:
00290     explicit choose_function(const args_list& args)
00291          : function_expression("choose", args, 2, 2)
00292     {}
00293 
00294 private:
00295     variant execute(const formula_callable& variables) const {
00296         const variant items = args()[0]->evaluate(variables);
00297         int max_index = -1;
00298         variant max_value;
00299         for(size_t n = 0; n != items.num_elements(); ++n) {
00300             const variant val = args()[1]->evaluate(formula_variant_callable_with_backup(items[n], variables));
00301             if(max_index == -1 || val > max_value) {
00302                 max_index = n;
00303                 max_value = val;
00304             }
00305         }
00306 
00307         if(max_index == -1) {
00308             return variant();
00309         } else {
00310             return items[max_index];
00311         }
00312     }
00313 };
00314 
00315 class wave_function : public function_expression {
00316 public:
00317     explicit wave_function(const args_list& args)
00318          : function_expression("wave", args, 1, 1)
00319     {}
00320 
00321 private:
00322     variant execute(const formula_callable& variables) const {
00323         const int value = args()[0]->evaluate(variables).as_int()%1000;
00324         const double angle = 2.0*3.141592653589*(static_cast<double>(value)/1000.0);
00325         return variant(static_cast<int>(sin(angle)*1000.0));
00326     }
00327 };
00328 
00329 namespace {
00330 class variant_comparator : public formula_callable {
00331     expression_ptr expr_;
00332     const formula_callable* fallback_;
00333     mutable variant a_, b_;
00334     variant get_value(const std::string& key) const {
00335         if(key == "a") {
00336             return a_;
00337         } else if(key == "b") {
00338             return b_;
00339         } else {
00340             return fallback_->query_value(key);
00341         }
00342     }
00343 
00344     void get_inputs(std::vector<formula_input>* inputs) const {
00345         fallback_->get_inputs(inputs);
00346     }
00347 public:
00348     variant_comparator(const expression_ptr& expr, const formula_callable& fallback) : expr_(expr), fallback_(&fallback)
00349     {}
00350 
00351     bool operator()(const variant& a, const variant& b) const {
00352         a_ = a;
00353         b_ = b;
00354         return expr_->evaluate(*this).as_bool();
00355     }
00356 };
00357 }
00358 
00359 class sort_function : public function_expression {
00360 public:
00361     explicit sort_function(const args_list& args)
00362          : function_expression("sort", args, 1, 2)
00363     {}
00364 
00365 private:
00366     variant execute(const formula_callable& variables) const {
00367         variant list = args()[0]->evaluate(variables);
00368         std::vector<variant> vars;
00369         vars.reserve(list.num_elements());
00370         for(size_t n = 0; n != list.num_elements(); ++n) {
00371             vars.push_back(list[n]);
00372         }
00373 
00374         if(args().size() == 1) {
00375             std::sort(vars.begin(), vars.end());
00376         } else {
00377             std::sort(vars.begin(), vars.end(), variant_comparator(args()[1], variables));
00378         }
00379 
00380         return variant(&vars);
00381     }
00382 };
00383 
00384 class filter_function : public function_expression {
00385 public:
00386     explicit filter_function(const args_list& args)
00387         : function_expression("filter", args, 2, 3)
00388     {}
00389 private:
00390     variant execute(const formula_callable& variables) const {
00391         std::vector<variant> vars;
00392         const variant items = args()[0]->evaluate(variables);
00393         if(args().size() == 2) {
00394             for(size_t n = 0; n != items.num_elements(); ++n) {
00395                 const variant val = args()[1]->evaluate(formula_variant_callable_with_backup(items[n], variables));
00396                 if(val.as_bool()) {
00397                     vars.push_back(items[n]);
00398                 }
00399             }
00400         } else {
00401             map_formula_callable self_callable;
00402             const std::string self = args()[1]->evaluate(variables).as_string();
00403             for(size_t n = 0; n != items.num_elements(); ++n) {
00404                 self_callable.add(self, items[n]);
00405                 const variant val = args()[2]->evaluate(formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(items[n], variables)));
00406                 if(val.as_bool()) {
00407                     vars.push_back(items[n]);
00408                 }
00409             }
00410         }
00411 
00412         return variant(&vars);
00413     }
00414 };
00415 
00416 class find_function : public function_expression {
00417 public:
00418     explicit find_function(const args_list& args)
00419         : function_expression("find", args, 2, 3)
00420     {}
00421 
00422 private:
00423     variant execute(const formula_callable& variables) const {
00424         const variant items = args()[0]->evaluate(variables);
00425 
00426         if(args().size() == 2) {
00427             for(size_t n = 0; n != items.num_elements(); ++n) {
00428                 const variant val = args()[1]->evaluate(formula_variant_callable_with_backup(items[n], variables));
00429                 if(val.as_bool()) {
00430                     return items[n];
00431                 }
00432             }
00433         } else {
00434             map_formula_callable self_callable;
00435             const std::string self = args()[1]->evaluate(variables).as_string();
00436             for(size_t n = 0; n != items.num_elements(); ++n) {
00437                 self_callable.add(self, items[n]);
00438                 const variant val = args().back()->evaluate(formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(items[n], variables)));
00439                 if(val.as_bool()) {
00440                     return items[n];
00441                 }
00442             }
00443         }
00444 
00445         return variant();
00446     }
00447 };
00448 
00449 class map_function : public function_expression {
00450 public:
00451     explicit map_function(const args_list& args)
00452         : function_expression("map", args, 2, 3)
00453     {}
00454 private:
00455     variant execute(const formula_callable& variables) const {
00456         std::vector<variant> vars;
00457         const variant items = args()[0]->evaluate(variables);
00458 
00459         if(args().size() == 2) {
00460             for(size_t n = 0; n != items.num_elements(); ++n) {
00461                 const variant val = args().back()->evaluate(formula_variant_callable_with_backup(items[n], variables));
00462                 vars.push_back(val);
00463             }
00464         } else {
00465             map_formula_callable self_callable;
00466             const std::string self = args()[1]->evaluate(variables).as_string();
00467             for(size_t n = 0; n != items.num_elements(); ++n) {
00468                 self_callable.add(self, items[n]);
00469                 const variant val = args().back()->evaluate(formula_callable_with_backup(self_callable, formula_variant_callable_with_backup(items[n], variables)));
00470                 vars.push_back(val);
00471             }
00472         }
00473 
00474         return variant(&vars);
00475     }
00476 };
00477 
00478 class sum_function : public function_expression {
00479 public:
00480     explicit sum_function(const args_list& args)
00481         : function_expression("sum", args, 1, 2)
00482     {}
00483 private:
00484     variant execute(const formula_callable& variables) const {
00485         variant res(0);
00486         const variant items = args()[0]->evaluate(variables);
00487         if(args().size() >= 2) {
00488             res = args()[1]->evaluate(variables);
00489         }
00490         for(size_t n = 0; n != items.num_elements(); ++n) {
00491             res = res + items[n];
00492         }
00493 
00494         return res;
00495     }
00496 };
00497 
00498 class head_function : public function_expression {
00499 public:
00500     explicit head_function(const args_list& args)
00501         : function_expression("head", args, 1, 1)
00502     {}
00503 private:
00504     variant execute(const formula_callable& variables) const {
00505         const variant items = args()[0]->evaluate(variables);
00506         return items[0];
00507     }
00508 };
00509 
00510 class size_function : public function_expression {
00511 public:
00512     explicit size_function(const args_list& args)
00513         : function_expression("size", args, 1, 1)
00514     {}
00515 private:
00516     variant execute(const formula_callable& variables) const {
00517         const variant items = args()[0]->evaluate(variables);
00518         return variant(static_cast<int>(items.num_elements()));
00519     }
00520 };
00521 
00522 class null_function : public function_expression {
00523 public:
00524     explicit null_function(const args_list& args)
00525         : function_expression("null", args, 0, 0)
00526     {}
00527 private:
00528     variant execute(const formula_callable& /*variables*/) const {
00529         return variant();
00530     }
00531 };
00532 
00533 class refcount_function : public function_expression {
00534 public:
00535     explicit refcount_function(const args_list& args)
00536         : function_expression("refcount", args, 1, 1)
00537     {}
00538 private:
00539     variant execute(const formula_callable& variables) const {
00540         return variant(args()[0]->evaluate(variables).refcount());
00541     }
00542 };
00543 
00544 class loc_function : public function_expression {
00545 public:
00546     explicit loc_function(const args_list& args)
00547       : function_expression("loc", args, 2, 2)
00548     {}
00549 private:
00550     variant execute(const formula_callable& variables) const {
00551         return variant(new location_callable(gamemap::location(args()[0]->evaluate(variables).as_int()-1, args()[1]->evaluate(variables).as_int()-1)));
00552     }
00553 };
00554 
00555 }
00556 
00557 formula_function_expression::formula_function_expression(const std::string& name, const args_list& args, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& arg_names)
00558   : function_expression(name, args, arg_names.size(), arg_names.size()),
00559     formula_(formula), precondition_(precondition), arg_names_(arg_names), star_arg_(-1)
00560 {
00561     for(size_t n = 0; n != arg_names_.size(); ++n) {
00562         if(arg_names_.empty() == false && arg_names_[n][arg_names_[n].size()-1] == '*') {
00563             arg_names_[n].resize(arg_names_[n].size()-1);
00564             star_arg_ = n;
00565             break;
00566         }
00567     }
00568 }
00569 
00570 variant formula_function_expression::execute(const formula_callable& variables) const
00571 {
00572     static std::string indent;
00573     indent += "  ";
00574     std::cerr << indent << "executing '" << formula_->str() << "'\n";
00575     const int begin_time = SDL_GetTicks();
00576     map_formula_callable callable;
00577     for(size_t n = 0; n != arg_names_.size(); ++n) {
00578         variant var = args()[n]->evaluate(variables);
00579         callable.add(arg_names_[n], var);
00580         if(static_cast<int>(n) == star_arg_) {
00581             callable.set_fallback(var.as_callable());
00582         }
00583     }
00584 
00585     if(precondition_) {
00586         if(!precondition_->execute(callable).as_bool()) {
00587             std::cerr << "FAILED function precondition for function '" << formula_->str() << "' with arguments: ";
00588             for(size_t n = 0; n != arg_names_.size(); ++n) {
00589                 std::cerr << "  arg " << (n+1) << ": " << args()[n]->evaluate(variables).to_debug_string() << "\n";
00590             }
00591         }
00592     }
00593 
00594     variant res = formula_->execute(callable);
00595     const int taken = SDL_GetTicks() - begin_time;
00596     std::cerr << indent << "returning: " << taken << "\n";
00597     indent.resize(indent.size() - 2);
00598 
00599     return res;
00600 }
00601 
00602 function_expression_ptr formula_function::generate_function_expression(const std::vector<expression_ptr>& args) const
00603 {
00604     return function_expression_ptr(new formula_function_expression(name_, args, formula_, precondition_, args_));
00605 }
00606 
00607 void function_symbol_table::add_formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args)
00608 {
00609     custom_formulas_[name] = formula_function(name, formula, precondition, args);
00610 }
00611 
00612 expression_ptr function_symbol_table::create_function(const std::string& fn, const std::vector<expression_ptr>& args) const
00613 {
00614     const std::map<std::string, formula_function>::const_iterator i = custom_formulas_.find(fn);
00615     if(i != custom_formulas_.end()) {
00616         return i->second.generate_function_expression(args);
00617     }
00618 
00619     return expression_ptr();
00620 }
00621 
00622 std::vector<std::string> function_symbol_table::get_function_names() const
00623 {
00624     std::vector<std::string> res;
00625     for(std::map<std::string, formula_function>::const_iterator iter = custom_formulas_.begin(); iter != custom_formulas_.end(); iter++ ) {
00626         res.push_back((*iter).first);
00627     }
00628     return res;
00629 }
00630 
00631 namespace {
00632 
00633 class base_function_creator {
00634 public:
00635     virtual expression_ptr create_function(const std::vector<expression_ptr>& args) const = 0;
00636     virtual ~base_function_creator() {}
00637 };
00638 
00639 template<typename T>
00640 class function_creator : public base_function_creator {
00641 public:
00642     virtual expression_ptr create_function(const std::vector<expression_ptr>& args) const {
00643         return expression_ptr(new T(args));
00644     }
00645     virtual ~function_creator() {}
00646 };
00647 
00648 typedef std::map<std::string, base_function_creator*> functions_map;
00649 
00650 functions_map& get_functions_map() {
00651 
00652     static functions_map functions_table;
00653 
00654     if(functions_table.empty()) {
00655 #define FUNCTION(name) functions_table[#name] = new function_creator<name##_function>();
00656         FUNCTION(dir);
00657         FUNCTION(if);
00658         FUNCTION(switch);
00659         FUNCTION(abs);
00660         FUNCTION(min);
00661         FUNCTION(max);
00662         FUNCTION(choose);
00663         FUNCTION(wave);
00664         FUNCTION(sort);
00665         FUNCTION(filter);
00666         FUNCTION(find);
00667         FUNCTION(map);
00668         FUNCTION(sum);
00669         FUNCTION(head);
00670         FUNCTION(rgb);
00671         FUNCTION(transition);
00672         FUNCTION(color_transition);
00673         FUNCTION(size);
00674         FUNCTION(null);
00675         FUNCTION(refcount);
00676         FUNCTION(loc);
00677         FUNCTION(keys);
00678         FUNCTION(values);
00679 #undef FUNCTION
00680     }
00681 
00682     return functions_table;
00683 }
00684 
00685 }
00686 
00687 expression_ptr create_function(const std::string& fn,
00688                                const std::vector<expression_ptr>& args,
00689                                const function_symbol_table* symbols)
00690 {
00691     if(symbols) {
00692         expression_ptr res(symbols->create_function(fn, args));
00693         if(res) {
00694             return res;
00695         }
00696     }
00697 
00698     std::cerr << "FN: '" << fn << "' " << fn.size() << "\n";
00699 
00700     functions_map::const_iterator i = get_functions_map().find(fn);
00701     if(i == get_functions_map().end()) {
00702         std::cerr << "no function '" << fn << "'\n";
00703         throw formula_error();
00704     }
00705 
00706     return i->second->create_function(args);
00707 }
00708 
00709 std::vector<std::string> builtin_function_names()
00710 {
00711     std::vector<std::string> res;
00712     const functions_map& m = get_functions_map();
00713     for(functions_map::const_iterator i = m.begin(); i != m.end(); ++i) {
00714         res.push_back(i->first);
00715     }
00716 
00717     return res;
00718 }
00719 
00720 }

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs