formula.cpp

Go to the documentation of this file.
00001 /* $Id: formula.cpp 25895 2008-04-17 18:57:13Z mordante $ */
00002 /*
00003    Copyright (C) 2007 by David White <dave.net>
00004    Part of the Silver Tree Project
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 or later.
00008    This program is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY.
00010 
00011    See the COPYING file for more details.
00012 */
00013 #include "global.hpp"
00014 
00015 #include <algorithm>
00016 #include <boost/lexical_cast.hpp>
00017 #include <cmath>
00018 #include <iostream>
00019 #include <vector>
00020 
00021 //#include "foreach.hpp"
00022 #include "formula.hpp"
00023 #include "formula_callable.hpp"
00024 #include "formula_function.hpp"
00025 #include "formula_tokenizer.hpp"
00026 #include "map_utils.hpp"
00027 
00028 namespace game_logic
00029 {
00030 
00031 void formula_callable::set_value(const std::string& key, const variant& /*value*/)
00032 {
00033     std::cerr << "ERROR: cannot set key '" << key << "' on object\n";
00034 }
00035 
00036 map_formula_callable::map_formula_callable(
00037     const formula_callable* fallback) : fallback_(fallback)
00038 {}
00039 
00040 map_formula_callable& map_formula_callable::add(const std::string& key,
00041                                                 const variant& value)
00042 {
00043     values_[key] = value;
00044     return *this;
00045 }
00046 
00047 variant map_formula_callable::get_value(const std::string& key) const
00048 {
00049     return map_get_value_default(values_, key,
00050             fallback_ ? fallback_->query_value(key) : variant());
00051 }
00052 
00053 void map_formula_callable::get_inputs(std::vector<formula_input>* inputs) const
00054 {
00055     if(fallback_) {
00056         fallback_->get_inputs(inputs);
00057     }
00058     for(std::map<std::string,variant>::const_iterator i = values_.begin(); i != values_.end(); ++i) {
00059         inputs->push_back(formula_input(i->first, FORMULA_READ_WRITE));
00060     }
00061 }
00062 
00063 void map_formula_callable::set_value(const std::string& key, const variant& value)
00064 {
00065     values_[key] = value;
00066 }
00067 
00068 namespace {
00069 
00070 class function_list_expression : public formula_expression {
00071 public:
00072     explicit function_list_expression(function_symbol_table *symbols)
00073         : symbols_(symbols)
00074     {}
00075 
00076 private:
00077     variant execute(const formula_callable& /*variables*/) const {
00078         std::vector<variant> res;
00079         std::vector<std::string> function_names = builtin_function_names();
00080         std::vector<std::string> more_function_names = symbols_->get_function_names();
00081         function_names.insert(function_names.end(), more_function_names.begin(), more_function_names.end());
00082         for(size_t i = 0; i < function_names.size(); i++) {
00083             res.push_back(variant(function_names[i]));
00084         }
00085         return variant(&res);
00086     }
00087 
00088     function_symbol_table* symbols_;
00089 };
00090 
00091 class list_expression : public formula_expression {
00092 public:
00093     explicit list_expression(const std::vector<expression_ptr>& items)
00094        : items_(items)
00095     {}
00096 
00097 private:
00098     variant execute(const formula_callable& variables) const {
00099         std::vector<variant> res;
00100         res.reserve(items_.size());
00101         for(std::vector<expression_ptr>::const_iterator i = items_.begin(); i != items_.end(); ++i) {
00102             res.push_back((*i)->evaluate(variables));
00103         }
00104 
00105         return variant(&res);
00106     }
00107 
00108     std::vector<expression_ptr> items_;
00109 };
00110 
00111 class map_expression : public formula_expression {
00112 public:
00113     explicit map_expression(const std::vector<expression_ptr>& items)
00114        : items_(items)
00115     {}
00116 
00117 private:
00118     variant execute(const formula_callable& variables) const {
00119         std::map<variant,variant> res;
00120         for(std::vector<expression_ptr>::const_iterator i = items_.begin(); ( i != items_.end() ) && ( i+1 != items_.end() ) ; i+=2) {
00121                 variant key = (*i)->evaluate(variables);
00122                 variant value = (*(i+1))->evaluate(variables);
00123                 res[ key ] = value;
00124         }
00125 
00126         return variant(&res);
00127     }
00128 
00129     std::vector<expression_ptr> items_;
00130 };
00131 
00132 class unary_operator_expression : public formula_expression {
00133 public:
00134     unary_operator_expression(const std::string& op, expression_ptr arg)
00135       : operand_(arg)
00136     {
00137         if(op == "not") {
00138             op_ = NOT;
00139         } else if(op == "-") {
00140             op_ = SUB;
00141         } else {
00142             std::cerr << "illegal unary operator: '" << op << "'\n";
00143             throw formula_error();
00144         }
00145     }
00146 private:
00147     variant execute(const formula_callable& variables) const {
00148         const variant res = operand_->evaluate(variables);
00149         switch(op_) {
00150         case NOT: 
00151             return res.as_bool() ? variant(0) : variant(1);
00152         case SUB: 
00153         default: 
00154             return -res;
00155         }
00156     }
00157     enum OP { NOT, SUB };
00158     OP op_;
00159     expression_ptr operand_;
00160 };
00161 
00162 class list_callable : public formula_callable {
00163     variant list_;
00164 public:
00165     explicit list_callable(const variant& list) : list_(list)
00166     {}
00167 
00168     void get_inputs(std::vector<formula_input>* inputs) const {
00169         inputs->push_back(formula_input("size", FORMULA_READ_WRITE));
00170         inputs->push_back(formula_input("empty", FORMULA_READ_WRITE));
00171         inputs->push_back(formula_input("first", FORMULA_READ_WRITE));
00172         inputs->push_back(formula_input("last", FORMULA_READ_WRITE));
00173     }
00174 
00175     variant get_value(const std::string& key) const {
00176         if(key == "size") {
00177             return variant(list_.num_elements());
00178         } else if(key == "empty") {
00179             return variant(list_.num_elements() == 0);
00180         } else if(key == "first") {
00181             if(list_.num_elements() > 0) {
00182                 return list_[0];
00183             } else {
00184                 return variant();
00185             }
00186         } else if(key == "last") {
00187             if(list_.num_elements() > 0) {
00188                 return list_[list_.num_elements()-1];
00189             } else {
00190                 return variant();
00191             }
00192         } else {
00193             return variant();
00194         }
00195     }
00196 };
00197 
00198 class dot_expression : public formula_expression {
00199 public:
00200     dot_expression(expression_ptr left, expression_ptr right)
00201        : left_(left), right_(right)
00202     {}
00203 private:
00204     variant execute(const formula_callable& variables) const {
00205         const variant left = left_->evaluate(variables);
00206         if(!left.is_callable()) {
00207             if(left.is_list()) {
00208                 return right_->evaluate(list_callable(left));
00209             }
00210 
00211             return left;
00212         }
00213 
00214         return right_->evaluate(*left.as_callable());
00215     }
00216 
00217     expression_ptr left_, right_;
00218 };
00219 
00220 class square_bracket_expression : public formula_expression { //TODO
00221 public:
00222     square_bracket_expression(expression_ptr left, expression_ptr key)
00223        : left_(left), key_(key)
00224     {}
00225 private:
00226     variant execute(const formula_callable& variables) const {
00227         const variant left = left_->evaluate(variables);
00228         const variant key = key_->evaluate(variables);
00229         if(left.is_list() || left.is_map()) {
00230             return left[ key ];
00231         } else {
00232             std::cerr << "illegal usage of operator []'\n";
00233             throw formula_error();
00234         }
00235     }
00236 
00237     expression_ptr left_, key_;
00238 };
00239 
00240 class operator_expression : public formula_expression {
00241 public:
00242     operator_expression(const std::string& op, expression_ptr left,
00243                                  expression_ptr right)
00244       : op_(OP(op[0])), left_(left), right_(right)
00245     {
00246         if(op == ">=") {
00247             op_ = GTE;
00248         } else if(op == "<=") {
00249             op_ = LTE;
00250         } else if(op == "!=") {
00251             op_ = NEQ;
00252         } else if(op == "and") {
00253             op_ = AND;
00254         } else if(op == "or") {
00255             op_ = OR;
00256         }
00257     }
00258 
00259 private:
00260     variant execute(const formula_callable& variables) const {
00261         const variant left = left_->evaluate(variables);
00262         const variant right = right_->evaluate(variables);
00263         switch(op_) {
00264         case AND: 
00265             return left.as_bool() == false ? left : right;
00266         case OR: 
00267             return left.as_bool() ? left : right;
00268         case ADD: 
00269             return left + right;
00270         case SUB: 
00271             return left - right;
00272         case MUL: 
00273             return left * right;
00274         case DIV: 
00275             return left / right;
00276         case POW: 
00277             return left ^ right;
00278         case EQ:  
00279             return left == right ? variant(1) : variant(0);
00280         case NEQ: 
00281             return left != right ? variant(1) : variant(0);
00282         case LTE: 
00283             return left <= right ? variant(1) : variant(0);
00284         case GTE: 
00285             return left >= right ? variant(1) : variant(0);
00286         case LT:  
00287             return left < right ? variant(1) : variant(0);
00288         case GT:  
00289             return left > right ? variant(1) : variant(0);
00290         case MOD: 
00291             return left % right;
00292         case DICE:
00293         default:
00294             return variant(dice_roll(left.as_int(), right.as_int()));
00295         }
00296     }
00297 
00298     static int dice_roll(int num_rolls, int faces) {
00299         int res = 0;
00300         while(faces > 0 && num_rolls-- > 0) {
00301             res += (rand()%faces)+1;
00302         }
00303         return res;
00304     }
00305 
00306     enum OP { AND, OR, NEQ, LTE, GTE, GT='>', LT='<', EQ='=',
00307               ADD='+', SUB='-', MUL='*', DIV='/', DICE='d', POW='^', MOD='%' };
00308 
00309     OP op_;
00310     expression_ptr left_, right_;
00311 };
00312 
00313 typedef std::map<std::string,expression_ptr> expr_table;
00314 typedef boost::shared_ptr<expr_table> expr_table_ptr;
00315 
00316 class where_variables: public formula_callable {
00317 public:
00318     where_variables(const formula_callable &base,
00319             expr_table_ptr table )
00320         : base_(base), table_(table) { }
00321 private:
00322     const formula_callable& base_;
00323     expr_table_ptr table_;
00324 
00325     void get_inputs(std::vector<formula_input>* inputs) const {
00326         for(expr_table::const_iterator i = table_->begin(); i != table_->end(); ++i) {
00327             inputs->push_back(formula_input(i->first, FORMULA_READ_ONLY));
00328         }
00329     }
00330 
00331     variant get_value(const std::string& key) const {
00332         expr_table::iterator i = table_->find(key);
00333         if(i != table_->end()) {
00334             return i->second->evaluate(base_);
00335         }
00336         return base_.query_value(key);
00337     }
00338 };
00339 
00340 class where_expression: public formula_expression {
00341 public:
00342     explicit where_expression(expression_ptr body,
00343                   expr_table_ptr clauses)
00344         : body_(body), clauses_(clauses)
00345     {}
00346 
00347 private:
00348     expression_ptr body_;
00349     expr_table_ptr clauses_;
00350 
00351     variant execute(const formula_callable& variables) const {
00352         where_variables wrapped_variables(variables, clauses_);
00353         return body_->evaluate(wrapped_variables);
00354     }
00355 };
00356 
00357 
00358 class identifier_expression : public formula_expression {
00359 public:
00360     explicit identifier_expression(const std::string& id) : id_(id)
00361     {}
00362 private:
00363     variant execute(const formula_callable& variables) const {
00364         return variables.query_value(id_);
00365     }
00366     std::string id_;
00367 };
00368 
00369 class null_expression : public formula_expression {
00370 public:
00371     explicit null_expression() {};
00372 private:
00373     variant execute(const formula_callable& /*variables*/) const {
00374         return variant();
00375     }
00376 };
00377 
00378 
00379 class integer_expression : public formula_expression {
00380 public:
00381     explicit integer_expression(int i) : i_(i)
00382     {}
00383 private:
00384     variant execute(const formula_callable& /*variables*/) const {
00385         return variant(i_);
00386     }
00387 
00388     int i_;
00389 };
00390 
00391 class string_expression : public formula_expression {
00392 public:
00393     explicit string_expression(std::string str)
00394     {
00395         std::string::iterator i;
00396         while((i = std::find(str.begin(), str.end(), '{')) != str.end()) {
00397             std::string::iterator j = std::find(i, str.end(), '}');
00398             if(j == str.end()) {
00399                 break;
00400             }
00401 
00402             const std::string formula_str(i+1, j);
00403             const int pos = i - str.begin();
00404             str.erase(i, j+1);
00405 
00406             substitution sub;
00407             sub.pos = pos;
00408             sub.calculation.reset(new formula(formula_str));
00409             subs_.push_back(sub);
00410         }
00411 
00412         std::reverse(subs_.begin(), subs_.end());
00413 
00414         str_ = variant(str);
00415     }
00416 private:
00417     variant execute(const formula_callable& variables) const {
00418         if(subs_.empty()) {
00419             return str_;
00420         } else {
00421             std::string res = str_.as_string();
00422             for(size_t i=0; i < subs_.size(); ++i) {
00423                 const substitution& sub = subs_[i];
00424                 const std::string str = sub.calculation->execute(variables).string_cast();
00425                 res.insert(sub.pos, str);
00426             }
00427 
00428             return variant(res);
00429         }
00430     }
00431 
00432     struct substitution {
00433         int pos;
00434         const_formula_ptr calculation;
00435     };
00436 
00437     variant str_;
00438     std::vector<substitution> subs_;
00439 };
00440 
00441 using namespace formula_tokenizer;
00442 int operator_precedence(const token& t)
00443 {
00444     static std::map<std::string,int> precedence_map;
00445     if(precedence_map.empty()) {
00446         int n = 0;
00447         precedence_map["not"] = ++n;
00448         precedence_map["where"] = ++n;
00449         precedence_map["or"]    = ++n;
00450         precedence_map["and"]   = ++n;
00451         precedence_map["="]     = ++n;
00452         precedence_map["!="]    = n;
00453         precedence_map["<"]     = n;
00454         precedence_map[">"]     = n;
00455         precedence_map["<="]    = n;
00456         precedence_map[">="]    = n;
00457         precedence_map["+"]     = ++n;
00458         precedence_map["-"]     = n;
00459         precedence_map["*"]     = ++n;
00460         precedence_map["/"]     = ++n;
00461         precedence_map["%"]     = ++n;
00462         precedence_map["^"]     = ++n;
00463         precedence_map["d"]     = ++n;
00464         precedence_map["."]     = ++n;
00465     }
00466 
00467     assert(precedence_map.count(std::string(t.begin,t.end)));
00468     return precedence_map[std::string(t.begin,t.end)];
00469 }
00470 
00471 expression_ptr parse_expression(const token* i1, const token* i2, function_symbol_table* symbols);
00472 
00473 void parse_function_args(const token* &i1, const token* i2,
00474         std::vector<std::string>* res)
00475 {
00476     if(i1->type == TOKEN_LPARENS) {
00477         ++i1;
00478     } else {
00479         std::cerr << "Invalid function definition" << std::endl;
00480         throw formula_error();
00481     }
00482 
00483     while((i1-> type != TOKEN_RPARENS) && (i1 != i2)) {
00484         if(i1->type == TOKEN_IDENTIFIER) {
00485             if(std::string((i1+1)->begin, (i1+1)->end) == "*") {
00486                 res->push_back(std::string(i1->begin, i1->end) + std::string("*"));
00487                 ++i1;
00488             } else {
00489                 res->push_back(std::string(i1->begin, i1->end));
00490             }
00491         } else if (i1->type == TOKEN_COMMA) {
00492             //do nothing
00493         } else {
00494             std::cerr << "Invalid function definition" << std::endl;
00495             throw formula_error();
00496         }
00497         ++i1;
00498     }
00499 
00500     if(i1->type != TOKEN_RPARENS) {
00501         std::cerr << "Invalid function definition" << std::endl;
00502         throw formula_error();
00503     }
00504     ++i1;
00505 }
00506 
00507 void parse_args(const token* i1, const token* i2,
00508                 std::vector<expression_ptr>* res,
00509                 function_symbol_table* symbols)
00510 {
00511     int parens = 0;
00512     const token* beg = i1;
00513     while(i1 != i2) {
00514         if(i1->type == TOKEN_LPARENS || i1->type == TOKEN_LSQUARE || i1->type == TOKEN_LBRACKET ) {
00515             ++parens;
00516         } else if(i1->type == TOKEN_RPARENS || i1->type == TOKEN_RSQUARE || i1->type == TOKEN_RBRACKET) {
00517             --parens;
00518         } else if(i1->type == TOKEN_COMMA && !parens) {
00519             res->push_back(parse_expression(beg,i1, symbols));
00520             beg = i1+1;
00521         }
00522 
00523         ++i1;
00524     }
00525 
00526     if(beg != i1) {
00527         res->push_back(parse_expression(beg,i1, symbols));
00528     }
00529 }
00530 
00531 void parse_set_args(const token* i1, const token* i2,
00532                 std::vector<expression_ptr>* res,
00533                 function_symbol_table* symbols)
00534 {
00535     int parens = 0;
00536     bool check_pointer = false;
00537     const token* beg = i1;
00538     while(i1 != i2) {
00539         if(i1->type == TOKEN_LPARENS || i1->type == TOKEN_LSQUARE) {
00540             ++parens;
00541         } else if(i1->type == TOKEN_RPARENS || i1->type == TOKEN_RSQUARE) {
00542             --parens;
00543         } else if( i1->type == TOKEN_POINTER && !parens ) {
00544             if (!check_pointer) {
00545                 check_pointer = true;
00546                 res->push_back(parse_expression(beg,i1, symbols));
00547                 beg = i1+1;
00548             } else {
00549                 std::cerr << "Too many '->' operators\n";
00550                 throw formula_error();
00551             }
00552         } else if( i1->type == TOKEN_COMMA && !parens ) {
00553             check_pointer = false;
00554             res->push_back(parse_expression(beg,i1, symbols));
00555             beg = i1+1;
00556         }
00557 
00558         ++i1;
00559     }
00560 
00561     if(beg != i1) {
00562         res->push_back(parse_expression(beg,i1, symbols));
00563     }
00564 }
00565 
00566 void parse_where_clauses(const token* i1, const token * i2,
00567                          expr_table_ptr res, function_symbol_table* symbols) {
00568     int parens = 0;
00569     const token *original_i1_cached = i1;
00570     const token *beg = i1;
00571     std::string var_name;
00572     while(i1 != i2) {
00573         if(i1->type == TOKEN_LPARENS) {
00574             ++parens;
00575         } else if(i1->type == TOKEN_RPARENS) {
00576             --parens;
00577         } else if(!parens) {
00578             if(i1->type == TOKEN_COMMA) {
00579                 if(var_name.empty()) {
00580                     std::cerr << "There is 'where <expression>,; "
00581                           << "'where name=<expression>,' was needed.\n";
00582                     throw formula_error();
00583                 }
00584                 (*res)[var_name] = parse_expression(beg,i1, symbols);
00585                 beg = i1+1;
00586                 var_name = "";
00587             } else if(i1->type == TOKEN_OPERATOR) {
00588                 std::string op_name(i1->begin, i1->end);
00589                 if(op_name == "=") {
00590                     if(beg->type != TOKEN_IDENTIFIER) {
00591                         if(i1 == original_i1_cached) {
00592                             std::cerr<< "There is 'where =<expression'; "
00593                                  << "'where name=<expression>' was needed.\n";
00594                         } else {
00595                             std::cerr<< "There is 'where <expression>=<expression>'; "
00596                                  << "'where name=<expression>' was needed.\n";
00597                         }
00598                         throw formula_error();
00599                     } else if(beg+1 != i1) {
00600                         std::cerr<<"There is 'where name <expression>=<expression>'; "
00601                              << "'where name=<expression>' was needed.\n";
00602                         throw formula_error();
00603                     } else if(!var_name.empty()) {
00604                         std::cerr<<"There is 'where name=name=<expression>'; "
00605                              <<"'where name=<expression>' was needed.\n";
00606                         throw formula_error();
00607                     }
00608                     var_name.insert(var_name.end(), beg->begin, beg->end);
00609                     beg = i1+1;
00610                 }
00611             }
00612         }
00613         ++i1;
00614     }
00615     if(beg != i1) {
00616         if(var_name.empty()) {
00617             std::cerr << "There is 'where <expression>'; "
00618                   << "'where name=<expression> was needed.\n";
00619             throw formula_error();
00620         }
00621         (*res)[var_name] = parse_expression(beg,i1, symbols);
00622     }
00623 }
00624 
00625 expression_ptr parse_expression(const token* i1, const token* i2, function_symbol_table* symbols)
00626 {
00627     if(i1 == i2) {
00628         std::cerr << "empty expression\n";
00629         throw formula_error();
00630     }
00631 
00632     if(i1->type == TOKEN_KEYWORD &&
00633             (i1+1)->type == TOKEN_IDENTIFIER) {
00634         if(std::string(i1->begin, i1->end) == "def") {
00635             ++i1;
00636             const std::string formula_name = std::string(i1->begin, i1->end);
00637             std::vector<std::string> args;
00638             parse_function_args(++i1, i2, &args);
00639             const token* beg = i1;
00640             while((i1 != i2) && (i1->type != TOKEN_SEMICOLON)) {
00641                 ++i1;
00642             }
00643             const std::string formula_str = std::string(beg->begin, (i1-1)->end);
00644             const std::string precond = "";
00645             symbols->add_formula_function(formula_name,
00646                     const_formula_ptr(new formula(formula_str, symbols)),
00647                     formula::create_optional_formula(precond, symbols),
00648                     args);
00649             if((i1 == i2) || (i1 == (i2-1))) {
00650                 return expression_ptr(new function_list_expression(symbols));
00651             }
00652             else {
00653                 return parse_expression((i1+1), i2, symbols);
00654             }
00655         }
00656     }
00657 
00658     int parens = 0;
00659     const token* op = NULL;
00660     for(const token* i = i1; i != i2; ++i) {
00661         if(i->type == TOKEN_LPARENS || i->type == TOKEN_LSQUARE) {
00662             ++parens;
00663         } else if(i->type == TOKEN_RPARENS || i->type == TOKEN_RSQUARE) {
00664             --parens;
00665         } else if(parens == 0 && i->type == TOKEN_OPERATOR) {
00666             if(op == NULL || operator_precedence(*op) >
00667                              operator_precedence(*i)) {
00668                 op = i;
00669             }
00670         }
00671     }
00672 
00673     if(op == NULL) {
00674         if(i1->type == TOKEN_LPARENS && (i2-1)->type == TOKEN_RPARENS) {
00675             return parse_expression(i1+1,i2-1,symbols);
00676         } else if( (i2-1)->type == TOKEN_RSQUARE) { //check if there is [ ] : either a list definition, or a operator 
00677                 const token* tok = i2-2;
00678                 int square_parens = 0;
00679                 while ( (tok->type != TOKEN_LSQUARE || square_parens) && tok != i1) {
00680                         if (tok->type == TOKEN_RSQUARE) {
00681                             square_parens++;
00682                         } else if(tok->type == TOKEN_LSQUARE) {
00683                             square_parens--;
00684                         }
00685                         --tok;
00686                 }   
00687                 if (tok->type == TOKEN_LSQUARE) {
00688                     if (tok == i1) {
00689                         //create a list
00690                         std::vector<expression_ptr> args;
00691                         parse_args(i1+1,i2-1,&args,symbols);
00692                         return expression_ptr(new list_expression(args));
00693                     } else {
00694                         //execute operator [ ]
00695                         return expression_ptr(new square_bracket_expression(
00696                                             parse_expression(i1,tok,symbols),
00697                                                 parse_expression(tok+1,i2-1,symbols)));
00698                     }
00699                 }
00700         } else if(i1->type == TOKEN_LBRACKET && (i2-1)->type == TOKEN_RBRACKET) {
00701                 //create a map TODO: add support for a set
00702                 std::vector<expression_ptr> args;
00703                 parse_set_args(i1+1,i2-1,&args,symbols);
00704                 return expression_ptr(new map_expression(args));
00705         } else if(i2 - i1 == 1) {
00706             if(i1->type == TOKEN_KEYWORD) {
00707                 if(std::string(i1->begin,i1->end) == "functions") {
00708                     return expression_ptr(new function_list_expression(symbols));
00709                 }
00710             } else if(i1->type == TOKEN_IDENTIFIER) {
00711                 return expression_ptr(new identifier_expression(
00712                                  std::string(i1->begin,i1->end)));
00713             } else if(i1->type == TOKEN_INTEGER) {
00714                 int n = boost::lexical_cast<int>(std::string(i1->begin,i1->end));
00715                 return expression_ptr(new integer_expression(n));
00716             } else if(i1->type == TOKEN_STRING_LITERAL) {
00717                 return expression_ptr(new string_expression(std::string(i1->begin+1,i1->end-1)));
00718             }
00719         } else if(i1->type == TOKEN_IDENTIFIER &&
00720                   (i1+1)->type == TOKEN_LPARENS &&
00721                   (i2-1)->type == TOKEN_RPARENS) {
00722             int nleft = 0, nright = 0;
00723             for(const token* i = i1; i != i2; ++i) {
00724                 if(i->type == TOKEN_LPARENS) {
00725                     ++nleft;
00726                 } else if(i->type == TOKEN_RPARENS) {
00727                     ++nright;
00728                 }
00729             }
00730 
00731             if(nleft == nright) {
00732                 std::vector<expression_ptr> args;
00733                 parse_args(i1+2,i2-1,&args,symbols);
00734                 return expression_ptr(
00735                   create_function(std::string(i1->begin,i1->end),args,symbols));
00736             }
00737         }
00738 
00739         std::ostringstream expr;
00740         while(i1 != i2) {
00741             expr << std::string(i1->begin,i1->end);
00742             ++i1;
00743         }
00744         std::cerr << "could not parse expression: '" << expr.str() << "'\n";
00745         throw formula_error();
00746     }
00747 
00748     if(op == i1) {
00749         return expression_ptr(new unary_operator_expression(
00750                                  std::string(op->begin,op->end),
00751                                  parse_expression(op+1,i2,symbols)));
00752     }
00753 
00754     const std::string op_name(op->begin,op->end);
00755 
00756     if(op_name == ".") {
00757         return expression_ptr(new dot_expression(
00758                                  parse_expression(i1,op,symbols),
00759                                  parse_expression(op+1,i2,symbols)));
00760     }
00761 
00762     if(op_name == "where") {
00763         expr_table_ptr table(new expr_table());
00764         parse_where_clauses(op+1, i2, table, symbols);
00765         return expression_ptr(new where_expression(parse_expression(i1, op, symbols),
00766                                table));
00767     }
00768 
00769     return expression_ptr(new operator_expression(
00770                                  op_name, parse_expression(i1,op,symbols),
00771                                  parse_expression(op+1,i2,symbols)));
00772 }
00773 
00774 }
00775 
00776 formula_ptr formula::create_string_formula(const std::string& str)
00777 {
00778     formula_ptr res(new formula());
00779     res->expr_.reset(new string_expression(str));
00780     return res;
00781 }
00782 
00783 formula_ptr formula::create_optional_formula(const std::string& str, function_symbol_table* symbols)
00784 {
00785     if(str.empty()) {
00786         return formula_ptr();
00787     }
00788 
00789     try {
00790         return formula_ptr(new formula(str, symbols));
00791     } catch(...) {
00792         std::cerr << "ERROR parsing optional formula: '" << str << "'\n";
00793         return formula_ptr();
00794     }
00795 }
00796 
00797 formula::formula(const std::string& str, function_symbol_table* symbols) : str_(str)
00798 {
00799     using namespace formula_tokenizer;
00800 
00801     std::vector<token> tokens;
00802     std::string::const_iterator i1 = str.begin(), i2 = str.end();
00803     while(i1 != i2) {
00804         try {
00805             tokens.push_back(get_token(i1,i2));
00806             if((tokens.back().type == TOKEN_WHITESPACE) || (tokens.back().type == TOKEN_COMMENT)) {
00807                 tokens.pop_back();
00808             }
00809         } catch(token_error& /*e*/) {
00810             throw formula_error();
00811         }
00812     }
00813 
00814     try {
00815         if(tokens.size() != 0) {
00816             expr_ = parse_expression(&tokens[0],&tokens[0] + tokens.size(), symbols);
00817         } else {
00818             expr_ = expression_ptr(new null_expression());
00819         }   
00820     } catch(...) {
00821         std::cerr << "error parsing formula '" << str << "'\n";
00822         throw;
00823     }
00824 }
00825 
00826 variant formula::execute(const formula_callable& variables) const
00827 {
00828     try {
00829         return expr_->evaluate(variables);
00830     } catch(type_error& e) {
00831         std::cerr << "formula type error: " << e.message << "\n";
00832         return variant();
00833     }
00834 }
00835 
00836 variant formula::execute() const
00837 {
00838     static map_formula_callable null_callable;
00839     return execute(null_callable);
00840 }
00841 
00842 }
00843 
00844 #ifdef UNIT_TEST_FORMULA
00845 using namespace game_logic;
00846 class mock_char : public formula_callable {
00847     variant get_value(const std::string& key) const {
00848         if(key == "strength") {
00849             return variant(15);
00850         } else if(key == "agility") {
00851             return variant(12);
00852         }
00853 
00854         return variant(10);
00855     }
00856 };
00857 class mock_party : public formula_callable {
00858     variant get_value(const std::string& key) const {
00859         if(key == "members") {
00860             i_[0].add("strength",variant(12));
00861             i_[1].add("strength",variant(16));
00862             i_[2].add("strength",variant(14));
00863             std::vector<variant> members;
00864             for(int n = 0; n != 3; ++n) {
00865                 members.push_back(variant(&i_[n]));
00866             }
00867 
00868             return variant(&members);
00869         } else if(key == "char") {
00870             return variant(&c_);
00871         } else {
00872             return variant(0);
00873         }
00874     }
00875 
00876     mock_char c_;
00877     mutable map_formula_callable i_[3];
00878 
00879 };
00880 
00881 #include <time.h>
00882 
00883 int main()
00884 {
00885     srand(time(NULL));
00886     mock_char c;
00887     mock_party p;
00888     try {
00889         assert(formula("strength").execute(c).as_int() == 15);
00890         assert(formula("17").execute(c).as_int() == 17);
00891         assert(formula("strength/2 + agility").execute(c).as_int() == 19);
00892         assert(formula("(strength+agility)/2").execute(c).as_int() == 13);
00893         assert(formula("strength > 12").execute(c).as_int() == 1);
00894         assert(formula("strength > 18").execute(c).as_int() == 0);
00895         assert(formula("if(strength > 12, 7, 2)").execute(c).as_int() == 7);
00896         assert(formula("if(strength > 18, 7, 2)").execute(c).as_int() == 2);
00897         assert(formula("2 and 1").execute(c).as_int() == 1);
00898         assert(formula("2 and 0").execute(c).as_int() == 0);
00899         assert(formula("2 or 0").execute(c).as_int() == 1);
00900         assert(formula("-5").execute(c).as_int() == -5);
00901         assert(formula("not 5").execute(c).as_int() == 0);
00902         assert(formula("not 0").execute(c).as_int() == 1);
00903         assert(formula("abs(5)").execute(c).as_int() == 5);
00904         assert(formula("abs(-5)").execute(c).as_int() == 5);
00905         assert(formula("min(3,5)").execute(c).as_int() == 3);
00906         assert(formula("min(5,2)").execute(c).as_int() == 2);
00907         assert(formula("max(3,5)").execute(c).as_int() == 5);
00908         assert(formula("max(5,2)").execute(c).as_int() == 5);
00909         assert(formula("max(4,5,[2,18,7])").execute(c).as_int() == 18);
00910         assert(formula("char.strength").execute(p).as_int() == 15);
00911         assert(formula("choose(members,strength).strength").execute(p).as_int() == 16);
00912         assert(formula("4^2").execute().as_int() == 16);
00913         assert(formula("2+3^3").execute().as_int() == 29);
00914         assert(formula("2*3^3+2").execute().as_int() == 56);
00915         assert(formula("9^3").execute().as_int() == 729);
00916         assert(formula("x*5 where x=1").execute().as_int() == 5);
00917         assert(formula("x*(a*b where a=2,b=1) where x=5").execute().as_int() == 10);
00918         assert(formula("char.strength * ability where ability=3").execute(p).as_int() == 45);
00919         assert(formula("'abcd' = 'abcd'").execute(p).as_bool() == true);
00920         assert(formula("'abcd' = 'acd'").execute(p).as_bool() == false);
00921         assert(formula("'strength, agility: {strength}, {agility}'").execute(c).as_string() ==
00922                        "strength, agility: 15, 12");
00923         const int dice_roll = formula("3d6").execute().as_int();
00924         assert(dice_roll >= 3 && dice_roll <= 18);
00925 
00926         assert(formula::create_string_formula("Your strength is {strength}")->execute(c).as_string() ==
00927                         "Your strength is 15");
00928         variant myarray = formula("[1,2,3]").execute();
00929         assert(myarray.num_elements() == 3);
00930         assert(myarray[0].as_int() == 1);
00931         assert(myarray[1].as_int() == 2);
00932         assert(myarray[2].as_int() == 3);
00933     } catch(formula_error& e) {
00934         std::cerr << "parse error\n";
00935     }
00936 }
00937 #endif

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