00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "global.hpp"
00014
00015 #include <boost/lexical_cast.hpp>
00016 #include <iostream>
00017 #include <set>
00018 #include <sstream>
00019
00020 #include "foreach.hpp"
00021 #include "formula_callable.hpp"
00022 #include "formula_function.hpp"
00023 #include "map_utils.hpp"
00024
00025 namespace game_logic
00026 {
00027
00028 void formula_callable::set_value(const std::string& key, const variant& )
00029 {
00030 std::cerr << "ERROR: cannot set key '" << key << "' on object\n";
00031 }
00032
00033
00034 map_formula_callable::map_formula_callable(
00035 const formula_callable* fallback) :
00036 formula_callable(false),
00037 values_(),
00038 fallback_(fallback)
00039 {}
00040
00041 map_formula_callable& map_formula_callable::add(const std::string& key,
00042 const variant& value)
00043 {
00044 values_[key] = value;
00045 return *this;
00046 }
00047
00048 variant map_formula_callable::get_value(const std::string& key) const
00049 {
00050 return map_get_value_default(values_, key,
00051 fallback_ ? fallback_->query_value(key) : variant());
00052 }
00053
00054 void map_formula_callable::get_inputs(std::vector<formula_input>* inputs) const
00055 {
00056 if(fallback_) {
00057 fallback_->get_inputs(inputs);
00058 }
00059 for(std::map<std::string,variant>::const_iterator i = values_.begin(); i != values_.end(); ++i) {
00060 inputs->push_back(formula_input(i->first, FORMULA_READ_WRITE));
00061 }
00062 }
00063
00064 void map_formula_callable::set_value(const std::string& key, const variant& value)
00065 {
00066 values_[key] = value;
00067 }
00068
00069 namespace {
00070
00071 class function_list_expression : public formula_expression {
00072 public:
00073 explicit function_list_expression(function_symbol_table *symbols)
00074 : symbols_(symbols)
00075 {}
00076
00077 virtual std::string str() const
00078 {
00079 return "{function_list_expression()}";
00080 }
00081 private:
00082 variant execute(const formula_callable& , formula_debugger * ) const {
00083 std::vector<variant> res;
00084 std::vector<std::string> function_names = builtin_function_names();
00085 std::vector<std::string> more_function_names = symbols_->get_function_names();
00086 function_names.insert(function_names.end(), more_function_names.begin(), more_function_names.end());
00087 for(size_t i = 0; i < function_names.size(); i++) {
00088 res.push_back(variant(function_names[i]));
00089 }
00090 return variant(&res);
00091 }
00092
00093 function_symbol_table* symbols_;
00094 };
00095
00096 class list_expression : public formula_expression {
00097 public:
00098 explicit list_expression(const std::vector<expression_ptr>& items)
00099 : items_(items)
00100 {}
00101
00102 private:
00103 variant execute(const formula_callable& variables, formula_debugger *fdb) const {
00104 std::vector<variant> res;
00105 res.reserve(items_.size());
00106 for(std::vector<expression_ptr>::const_iterator i = items_.begin(); i != items_.end(); ++i) {
00107 res.push_back((*i)->evaluate(variables,fdb));
00108 }
00109
00110 return variant(&res);
00111 }
00112
00113 std::vector<expression_ptr> items_;
00114
00115 std::string str() const
00116 {
00117 std::stringstream s;
00118 s << '[';
00119 bool first_item = true;
00120 foreach(expression_ptr a , items_) {
00121 if (!first_item) {
00122 s << ',';
00123 } else {
00124 first_item = false;
00125 }
00126 s << a->str();
00127 }
00128 s << ']';
00129 return s.str();
00130 }
00131
00132
00133 };
00134
00135 class map_expression : public formula_expression {
00136 public:
00137 explicit map_expression(const std::vector<expression_ptr>& items)
00138 : items_(items)
00139 {}
00140
00141 virtual std::string str() const
00142 {
00143 std::stringstream s;
00144 s << " [";
00145 for(std::vector<expression_ptr>::const_iterator i = items_.begin(); ( i != items_.end() ) && ( i+1 != items_.end() ) ; i+=2) {
00146 s << (*i)->str();
00147 s << " -> ";
00148 s << (*(i+1))->str();
00149 }
00150 s << " ]";
00151 return s.str();
00152 }
00153 private:
00154 variant execute(const formula_callable& variables, formula_debugger *fdb) const {
00155 std::map<variant,variant> res;
00156 for(std::vector<expression_ptr>::const_iterator i = items_.begin(); ( i != items_.end() ) && ( i+1 != items_.end() ) ; i+=2) {
00157 variant key = (*i)->evaluate(variables,fdb);
00158 variant value = (*(i+1))->evaluate(variables,fdb);
00159 res[ key ] = value;
00160 }
00161
00162 return variant(&res);
00163 }
00164
00165 std::vector<expression_ptr> items_;
00166 };
00167
00168 class unary_operator_expression : public formula_expression {
00169 public:
00170 unary_operator_expression(const std::string& op, expression_ptr arg) :
00171 op_(),op_str_(op),
00172 operand_(arg)
00173 {
00174 if(op == "not") {
00175 op_ = NOT;
00176 } else if(op == "-") {
00177 op_ = SUB;
00178 } else {
00179 throw formula_error("Illegal unary operator: '" + op + "'" , "", "", 0);
00180 }
00181 }
00182
00183 virtual std::string str() const {
00184 std::stringstream s;
00185 s << op_str_ << '('<< operand_->str() << ')';
00186 return s.str();
00187 }
00188
00189 private:
00190 variant execute(const formula_callable& variables, formula_debugger *fdb) const {
00191 const variant res = operand_->evaluate(variables,fdb);
00192 switch(op_) {
00193 case NOT:
00194 return res.as_bool() ? variant(0) : variant(1);
00195 case SUB:
00196 default:
00197 return -res;
00198 }
00199 }
00200 enum OP { NOT, SUB };
00201 OP op_;
00202 std::string op_str_;
00203 expression_ptr operand_;
00204 };
00205
00206 class list_callable : public formula_callable {
00207 variant list_;
00208 public:
00209 explicit list_callable(const variant& list) : list_(list)
00210 {}
00211
00212 void get_inputs(std::vector<formula_input>* inputs) const {
00213 inputs->push_back(formula_input("size", FORMULA_READ_WRITE));
00214 inputs->push_back(formula_input("empty", FORMULA_READ_WRITE));
00215 inputs->push_back(formula_input("first", FORMULA_READ_WRITE));
00216 inputs->push_back(formula_input("last", FORMULA_READ_WRITE));
00217 }
00218
00219 variant get_value(const std::string& key) const {
00220 if(key == "size") {
00221 return variant(list_.num_elements());
00222 } else if(key == "empty") {
00223 return variant(list_.num_elements() == 0);
00224 } else if(key == "first") {
00225 if(list_.num_elements() > 0) {
00226 return list_[0];
00227 } else {
00228 return variant();
00229 }
00230 } else if(key == "last") {
00231 if(list_.num_elements() > 0) {
00232 return list_[list_.num_elements()-1];
00233 } else {
00234 return variant();
00235 }
00236 } else {
00237 return variant();
00238 }
00239 }
00240
00241 };
00242
00243 class dot_callable : public formula_callable {
00244 public:
00245 dot_callable(const formula_callable &global,
00246 const formula_callable& local)
00247 : global_(global), local_(local) { }
00248 private:
00249 const formula_callable& global_, &local_;
00250
00251 void get_inputs(std::vector<formula_input>* inputs) const {
00252 return local_.get_inputs(inputs);
00253 }
00254
00255 variant get_value(const std::string& key) const {
00256 variant v = local_.query_value(key);
00257
00258 if ( v == variant() )
00259 return global_.query_value(key);
00260 else
00261 return v;
00262 }
00263 };
00264
00265 class dot_expression : public formula_expression {
00266 public:
00267 dot_expression(expression_ptr left, expression_ptr right)
00268 : left_(left), right_(right)
00269 {}
00270 std::string str() const
00271 {
00272 std::stringstream s;
00273 s << left_->str() << "." << right_->str();
00274 return s.str();
00275 }
00276 private:
00277 variant execute(const formula_callable& variables, formula_debugger *fdb) const {
00278 const variant left = left_->evaluate(variables,add_debug_info(fdb,0,"left."));
00279 if(!left.is_callable()) {
00280 if(left.is_list()) {
00281 list_callable list_call(left);
00282 dot_callable callable(variables, list_call);
00283 return right_->evaluate(callable,fdb);
00284 }
00285
00286 return left;
00287 }
00288
00289 dot_callable callable(variables, *left.as_callable());
00290 return right_->evaluate(callable,add_debug_info(fdb,1,".right"));
00291 }
00292
00293 expression_ptr left_, right_;
00294 };
00295
00296 class square_bracket_expression : public formula_expression {
00297 public:
00298 square_bracket_expression(expression_ptr left, expression_ptr key)
00299 : left_(left), key_(key)
00300 {}
00301
00302 std::string str() const
00303 {
00304 std::stringstream s;
00305 s << left_->str() << '[' << key_->str() << ']';
00306 return s.str();
00307 }
00308 private:
00309 variant execute(const formula_callable& variables, formula_debugger *fdb) const {
00310 const variant left = left_->evaluate(variables,fdb);
00311 const variant key = key_->evaluate(variables,fdb);
00312 if(left.is_list() || left.is_map()) {
00313 return left[ key ];
00314 } else {
00315 return variant();
00316 }
00317 }
00318
00319 expression_ptr left_, key_;
00320 };
00321
00322 class operator_expression : public formula_expression {
00323 public:
00324 operator_expression(const std::string& op, expression_ptr left,
00325 expression_ptr right)
00326 : op_(OP(op[0])), op_str_(op), left_(left), right_(right)
00327 {
00328 if(op == ">=") {
00329 op_ = GTE;
00330 } else if(op == "<=") {
00331 op_ = LTE;
00332 } else if(op == "!=") {
00333 op_ = NEQ;
00334 } else if(op == "and") {
00335 op_ = AND;
00336 } else if(op == "or") {
00337 op_ = OR;
00338 } else if(op == ".+") {
00339 op_ = ADDL;
00340 } else if(op == ".-") {
00341 op_ = SUBL;
00342 } else if(op == ".*") {
00343 op_ = MULL;
00344 } else if(op == "./") {
00345 op_ = DIVL;
00346 }
00347 }
00348
00349 std::string str() const
00350 {
00351 std::stringstream s;
00352 s << left_->str() << op_str_ << right_->str();
00353 return s.str();
00354 }
00355 private:
00356 variant execute(const formula_callable& variables, formula_debugger *fdb) const {
00357 const variant left = left_->evaluate(variables,add_debug_info(fdb,0,"left_OP"));
00358 const variant right = right_->evaluate(variables,add_debug_info(fdb,1,"OP_right"));
00359 switch(op_) {
00360 case AND:
00361 return left.as_bool() == false ? left : right;
00362 case OR:
00363 return left.as_bool() ? left : right;
00364 case ADD:
00365 return left + right;
00366 case SUB:
00367 return left - right;
00368 case MUL:
00369 return left * right;
00370 case DIV:
00371 return left / right;
00372 case POW:
00373 return left ^ right;
00374 case ADDL:
00375 return left.list_elements_add(right);
00376 case SUBL:
00377 return left.list_elements_sub(right);
00378 case MULL:
00379 return left.list_elements_mul(right);
00380 case DIVL:
00381 return left.list_elements_div(right);
00382 case EQ:
00383 return left == right ? variant(1) : variant(0);
00384 case NEQ:
00385 return left != right ? variant(1) : variant(0);
00386 case LTE:
00387 return left <= right ? variant(1) : variant(0);
00388 case GTE:
00389 return left >= right ? variant(1) : variant(0);
00390 case LT:
00391 return left < right ? variant(1) : variant(0);
00392 case GT:
00393 return left > right ? variant(1) : variant(0);
00394 case MOD:
00395 return left % right;
00396 case DICE:
00397 default:
00398 return variant(dice_roll(left.as_int(), right.as_int()));
00399 }
00400 }
00401
00402 static int dice_roll(int num_rolls, int faces) {
00403 int res = 0;
00404 while(faces > 0 && num_rolls-- > 0) {
00405 res += (rand()%faces)+1;
00406 }
00407 return res;
00408 }
00409
00410 enum OP { AND, OR, NEQ, LTE, GTE, GT='>', LT='<', EQ='=',
00411 ADD='+', SUB='-', MUL='*', DIV='/', ADDL, SUBL, MULL, DIVL, DICE='d', POW='^', MOD='%' };
00412
00413 OP op_;
00414 std::string op_str_;
00415 expression_ptr left_, right_;
00416 };
00417
00418 typedef std::map<std::string,expression_ptr> expr_table;
00419 typedef boost::shared_ptr<expr_table> expr_table_ptr;
00420 typedef std::map<std::string, variant> exp_table_evaluated;
00421
00422 class where_variables: public formula_callable {
00423 public:
00424 where_variables(const formula_callable &base,
00425 expr_table_ptr table )
00426 : formula_callable(false)
00427 , base_(base)
00428 , table_(table)
00429 , evaluated_table_()
00430 {
00431 }
00432
00433 private:
00434 const formula_callable& base_;
00435 expr_table_ptr table_;
00436 mutable exp_table_evaluated evaluated_table_;
00437
00438 void get_inputs(std::vector<formula_input>* inputs) const {
00439 for(expr_table::const_iterator i = table_->begin(); i != table_->end(); ++i) {
00440 inputs->push_back(formula_input(i->first, FORMULA_READ_ONLY));
00441 }
00442 }
00443
00444 variant get_value(const std::string& key) const {
00445 expr_table::iterator i = table_->find(key);
00446 if(i != table_->end()) {
00447 exp_table_evaluated::const_iterator ev = evaluated_table_.find(key);
00448 if( ev != evaluated_table_.end())
00449 return ev->second;
00450
00451 variant v = i->second->evaluate(base_);
00452 evaluated_table_[key] = v;
00453 return v;
00454 }
00455 return base_.query_value(key);
00456 }
00457 };
00458
00459 class where_expression: public formula_expression {
00460 public:
00461 explicit where_expression(expression_ptr body,
00462 expr_table_ptr clauses)
00463 : body_(body), clauses_(clauses)
00464 {}
00465
00466 std::string str() const
00467 {
00468 std::stringstream s;
00469 s << "{where:(";
00470 s << body_->str();
00471 foreach (const expr_table::value_type &a, *clauses_) {
00472 s << ", [" << a.first << "] -> ["<< a.second->str()<<"]";
00473 }
00474 s << ")}";
00475 return s.str();
00476 }
00477
00478 private:
00479 expression_ptr body_;
00480 expr_table_ptr clauses_;
00481
00482 variant execute(const formula_callable& variables,formula_debugger *fdb) const {
00483 where_variables wrapped_variables(variables, clauses_);
00484 return body_->evaluate(wrapped_variables,fdb);
00485 }
00486 };
00487
00488
00489 class identifier_expression : public formula_expression {
00490 public:
00491 explicit identifier_expression(const std::string& id) : id_(id)
00492 {}
00493 std::string str() const
00494 {
00495 return id_;
00496 }
00497 private:
00498 variant execute(const formula_callable& variables, formula_debugger * ) const {
00499 return variables.query_value(id_);
00500 }
00501 std::string id_;
00502 };
00503
00504 class null_expression : public formula_expression {
00505 public:
00506 explicit null_expression() {};
00507 std::string str() const {
00508 return "";
00509 }
00510 private:
00511 variant execute(const formula_callable& , formula_debugger * ) const {
00512 return variant();
00513 }
00514 };
00515
00516
00517 class integer_expression : public formula_expression {
00518 public:
00519 explicit integer_expression(int i) : i_(i)
00520 {}
00521 std::string str() const
00522 {
00523 std::stringstream s;
00524 s << i_;
00525 return s.str();
00526 }
00527 private:
00528 variant execute(const formula_callable& , formula_debugger * ) const {
00529 return variant(i_);
00530 }
00531
00532 int i_;
00533 };
00534
00535 class decimal_expression : public formula_expression {
00536 public:
00537 explicit decimal_expression(int i, int f) : i_(i), f_(f)
00538 {}
00539
00540 std::string str() const
00541 {
00542 std::stringstream s;
00543 s << i_ << '.' << f_;
00544 return s.str();
00545 }
00546 private:
00547 variant execute(const formula_callable& , formula_debugger * ) const {
00548 return variant(i_ * 1000 + f_, variant::DECIMAL_VARIANT );
00549 }
00550
00551 int i_, f_;
00552 };
00553
00554 class string_expression : public formula_expression {
00555 public:
00556 explicit string_expression(std::string str) :
00557 str_(),
00558 subs_()
00559 {
00560 std::string::iterator i;
00561 while((i = std::find(str.begin(), str.end(), '{')) != str.end()) {
00562 std::string::iterator j = std::find(i, str.end(), '}');
00563 if(j == str.end()) {
00564 break;
00565 }
00566
00567 const std::string formula_str(i+1, j);
00568 const int pos = i - str.begin();
00569 str.erase(i, j+1);
00570
00571 substitution sub;
00572 sub.pos = pos;
00573 sub.calculation.reset(new formula(formula_str));
00574 subs_.push_back(sub);
00575 }
00576
00577 std::reverse(subs_.begin(), subs_.end());
00578
00579 str_ = variant(str);
00580 }
00581
00582 std::string str() const
00583 {
00584 return str_.as_string();
00585 }
00586 private:
00587 variant execute(const formula_callable& variables, formula_debugger *fdb) const {
00588 if(subs_.empty()) {
00589 return str_;
00590 } else {
00591 std::string res = str_.as_string();
00592 for(size_t i=0; i < subs_.size(); ++i) {
00593 const substitution& sub = subs_[i];
00594 const std::string str = sub.calculation->evaluate(variables,fdb).string_cast();
00595 res.insert(sub.pos, str);
00596 }
00597
00598 return variant(res);
00599 }
00600 }
00601
00602 struct substitution {
00603
00604 substitution() :
00605 pos(0),
00606 calculation()
00607 {
00608 }
00609
00610 int pos;
00611 const_formula_ptr calculation;
00612 };
00613
00614 variant str_;
00615 std::vector<substitution> subs_;
00616 };
00617
00618 using namespace formula_tokenizer;
00619 int operator_precedence(const token& t)
00620 {
00621 static std::map<std::string,int> precedence_map;
00622 if(precedence_map.empty()) {
00623 int n = 0;
00624 precedence_map["not"] = ++n;
00625 precedence_map["where"] = ++n;
00626 precedence_map["or"] = ++n;
00627 precedence_map["and"] = ++n;
00628 precedence_map["="] = ++n;
00629 precedence_map["!="] = n;
00630 precedence_map["<"] = n;
00631 precedence_map[">"] = n;
00632 precedence_map["<="] = n;
00633 precedence_map[">="] = n;
00634 precedence_map["+"] = ++n;
00635 precedence_map["-"] = n;
00636 precedence_map["*"] = ++n;
00637 precedence_map["/"] = ++n;
00638 precedence_map["%"] = ++n;
00639 precedence_map["^"] = ++n;
00640 precedence_map["d"] = ++n;
00641 precedence_map["."] = ++n;
00642 }
00643
00644 assert(precedence_map.count(std::string(t.begin,t.end)));
00645 return precedence_map[std::string(t.begin,t.end)];
00646 }
00647
00648 expression_ptr parse_expression(const token* i1, const token* i2, function_symbol_table* symbols);
00649
00650
00651
00652 std::string tokens_to_string(const token* i1, const token* i2)
00653 {
00654 std::ostringstream expr;
00655 while(i1 != i2) {
00656 expr << std::string(i1->begin,i1->end) << " ";
00657 ++i1;
00658 }
00659 return expr.str();
00660 }
00661
00662 void parse_function_args(const token* &i1, const token* i2,
00663 std::vector<std::string>* res)
00664 {
00665 const token* begin = i1, *end = i2;
00666
00667 if(i1->type == TOKEN_LPARENS) {
00668 ++i1;
00669 } else {
00670 throw formula_error("Invalid function definition", tokens_to_string(begin,end-1), *i1->filename, i1->line_number);
00671 }
00672
00673 while((i1-> type != TOKEN_RPARENS) && (i1 != i2)) {
00674 if(i1->type == TOKEN_IDENTIFIER) {
00675 if(std::string((i1+1)->begin, (i1+1)->end) == "*") {
00676 res->push_back(std::string(i1->begin, i1->end) + std::string("*"));
00677 ++i1;
00678 } else {
00679 res->push_back(std::string(i1->begin, i1->end));
00680 }
00681 } else if (i1->type == TOKEN_COMMA) {
00682
00683 } else {
00684 throw formula_error("Invalid function definition", tokens_to_string(begin,end-1), *i1->filename, i1->line_number);
00685 }
00686 ++i1;
00687 }
00688
00689 if(i1->type != TOKEN_RPARENS) {
00690 throw formula_error("Invalid function definition", tokens_to_string(begin,end-1), *i1->filename, i1->line_number);
00691 }
00692 ++i1;
00693 }
00694
00695 void parse_args(const token* i1, const token* i2,
00696 std::vector<expression_ptr>* res,
00697 function_symbol_table* symbols)
00698 {
00699 int parens = 0;
00700 const token* beg = i1;
00701 while(i1 != i2) {
00702 if(i1->type == TOKEN_LPARENS || i1->type == TOKEN_LSQUARE ) {
00703 ++parens;
00704 } else if(i1->type == TOKEN_RPARENS || i1->type == TOKEN_RSQUARE ) {
00705 --parens;
00706 } else if(i1->type == TOKEN_COMMA && !parens) {
00707 res->push_back(parse_expression(beg,i1, symbols));
00708 beg = i1+1;
00709 }
00710 ++i1;
00711 }
00712
00713 if(beg != i1) {
00714 res->push_back(parse_expression(beg,i1, symbols));
00715 }
00716 }
00717
00718 void parse_set_args(const token* i1, const token* i2,
00719 std::vector<expression_ptr>* res,
00720 function_symbol_table* symbols)
00721 {
00722 int parens = 0;
00723 bool check_pointer = false;
00724 const token* beg = i1;
00725 const token* begin = i1, *end = i2;
00726 while(i1 != i2) {
00727 if(i1->type == TOKEN_LPARENS || i1->type == TOKEN_LSQUARE) {
00728 ++parens;
00729 } else if(i1->type == TOKEN_RPARENS || i1->type == TOKEN_RSQUARE) {
00730 --parens;
00731 } else if( i1->type == TOKEN_POINTER && !parens ) {
00732 if (!check_pointer) {
00733 check_pointer = true;
00734 res->push_back(parse_expression(beg,i1, symbols));
00735 beg = i1+1;
00736 } else {
00737 throw formula_error("Too many '->' operators found", tokens_to_string(begin,end-1), *i1->filename, i1->line_number);
00738 }
00739 } else if( i1->type == TOKEN_COMMA && !parens ) {
00740 if (check_pointer)
00741 check_pointer = false;
00742 else {
00743 throw formula_error("Expected comma, but '->' found", tokens_to_string(begin,end-1), *i1->filename, i1->line_number);
00744 }
00745 res->push_back(parse_expression(beg,i1, symbols));
00746 beg = i1+1;
00747 }
00748
00749 ++i1;
00750 }
00751
00752 if(beg != i1) {
00753 res->push_back(parse_expression(beg,i1, symbols));
00754 }
00755 }
00756
00757 void parse_where_clauses(const token* i1, const token * i2,
00758 expr_table_ptr res, function_symbol_table* symbols) {
00759 int parens = 0;
00760 const token *original_i1_cached = i1;
00761 const token *beg = i1;
00762 const token* begin = i1, *end = i2;
00763 std::string var_name;
00764 while(i1 != i2) {
00765 if(i1->type == TOKEN_LPARENS) {
00766 ++parens;
00767 } else if(i1->type == TOKEN_RPARENS) {
00768 --parens;
00769 } else if(!parens) {
00770 if(i1->type == TOKEN_COMMA) {
00771 if(var_name.empty()) {
00772 throw formula_error("There is 'where <expression>' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->filename, i1->line_number);
00773 }
00774 (*res)[var_name] = parse_expression(beg,i1, symbols);
00775 beg = i1+1;
00776 var_name = "";
00777 } else if(i1->type == TOKEN_OPERATOR) {
00778 std::string op_name(i1->begin, i1->end);
00779 if(op_name == "=") {
00780 if(beg->type != TOKEN_IDENTIFIER) {
00781 if(i1 == original_i1_cached) {
00782 throw formula_error("There is 'where <expression' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->filename, i1->line_number);
00783 } else {
00784 throw formula_error("There is 'where <expression>=<expression>' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->filename, i1->line_number);
00785 }
00786 } else if(beg+1 != i1) {
00787 throw formula_error("There is 'where name <expression>=<expression>' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->filename, i1->line_number);
00788 } else if(!var_name.empty()) {
00789 throw formula_error("There is 'where name=name=<expression>' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->filename, i1->line_number);
00790 }
00791 var_name.insert(var_name.end(), beg->begin, beg->end);
00792 beg = i1+1;
00793 }
00794 }
00795 }
00796 ++i1;
00797 }
00798 if(beg != i1) {
00799 if(var_name.empty()) {
00800 throw formula_error("There is 'where <expression>' but 'where name=<expression>' was needed", tokens_to_string(begin,end-1), *i1->filename, i1->line_number);
00801 }
00802 (*res)[var_name] = parse_expression(beg,i1, symbols);
00803 }
00804 }
00805
00806 expression_ptr parse_expression(const token* i1, const token* i2, function_symbol_table* symbols)
00807 {
00808 if(i1 == i2) {
00809 throw formula_error("Empty expression", "", *i1->filename, i1->line_number);
00810 }
00811
00812 const token* begin = i1, *end = i2;
00813
00814 if(i1->type == TOKEN_KEYWORD &&
00815 (i1+1)->type == TOKEN_IDENTIFIER) {
00816 if(std::string(i1->begin, i1->end) == "def") {
00817 ++i1;
00818 const std::string formula_name = std::string(i1->begin, i1->end);
00819 std::vector<std::string> args;
00820 parse_function_args(++i1, i2, &args);
00821 const token* beg = i1;
00822 while((i1 != i2) && (i1->type != TOKEN_SEMICOLON)) {
00823 ++i1;
00824 }
00825 const std::string precond = "";
00826 if(symbols == NULL) {
00827 throw formula_error("Function symbol table required but not present", "",*i1->filename, i1->line_number);
00828 }
00829 symbols->add_formula_function(formula_name,
00830 const_formula_ptr(new formula(beg, i1, symbols)),
00831 formula::create_optional_formula(precond, symbols),
00832 args);
00833 if((i1 == i2) || (i1 == (i2-1))) {
00834 return expression_ptr(new function_list_expression(symbols));
00835 }
00836 else {
00837 return parse_expression((i1+1), i2, symbols);
00838 }
00839 }
00840 }
00841
00842 int parens = 0;
00843 const token* op = NULL;
00844 bool operator_group = false;
00845 for(const token* i = i1; i != i2; ++i) {
00846 if(i->type == TOKEN_LPARENS || i->type == TOKEN_LSQUARE) {
00847 ++parens;
00848 } else if(i->type == TOKEN_RPARENS || i->type == TOKEN_RSQUARE) {
00849 --parens;
00850 } else if(parens == 0 && i->type == TOKEN_OPERATOR) {
00851 if( ( !operator_group ) && (op == NULL || operator_precedence(*op) >=
00852 operator_precedence(*i)) ) {
00853 op = i;
00854 }
00855 operator_group = true;
00856 } else {
00857 operator_group = false;
00858 }
00859 }
00860
00861 if(op == NULL) {
00862 if(i1->type == TOKEN_LPARENS && (i2-1)->type == TOKEN_RPARENS) {
00863 return parse_expression(i1+1,i2-1,symbols);
00864 } else if( (i2-1)->type == TOKEN_RSQUARE) {
00865 const token* tok = i2-2;
00866 int square_parens = 0;
00867 bool is_map = false;
00868 while ( (tok->type != TOKEN_LSQUARE || square_parens) && tok != i1) {
00869 if (tok->type == TOKEN_RSQUARE) {
00870 square_parens++;
00871 } else if(tok->type == TOKEN_LSQUARE) {
00872 square_parens--;
00873 } else if( (tok->type == TOKEN_POINTER) && !square_parens ) {
00874 is_map = true;
00875 }
00876 --tok;
00877 }
00878 if (tok->type == TOKEN_LSQUARE) {
00879 if (tok == i1) {
00880
00881 std::vector<expression_ptr> args;
00882
00883 if ( is_map ) {
00884 parse_set_args(i1+1, i2-1, &args, symbols);
00885 return expression_ptr(new map_expression(args));
00886 } else {
00887 parse_args(i1+1,i2-1,&args,symbols);
00888 return expression_ptr(new list_expression(args));
00889 }
00890 } else {
00891
00892 try{
00893 return expression_ptr(new square_bracket_expression(
00894 parse_expression(i1,tok,symbols),
00895 parse_expression(tok+1,i2-1,symbols)));
00896 }
00897 catch (formula_error& e){
00898 throw formula_error( e.type, tokens_to_string(i1, i2-1), *i1->filename, i1->line_number );
00899 }
00900 }
00901 }
00902 } else if(i2 - i1 == 1) {
00903 if(i1->type == TOKEN_KEYWORD) {
00904 if(std::string(i1->begin,i1->end) == "functions") {
00905 return expression_ptr(new function_list_expression(symbols));
00906 }
00907 } else if(i1->type == TOKEN_IDENTIFIER) {
00908 return expression_ptr(new identifier_expression(
00909 std::string(i1->begin,i1->end)));
00910 } else if(i1->type == TOKEN_INTEGER) {
00911 int n = atoi(std::string(i1->begin,i1->end).c_str());
00912 return expression_ptr(new integer_expression(n));
00913 } else if(i1->type == TOKEN_DECIMAL) {
00914 iterator dot = i1->begin;
00915 while( *dot != '.' )
00916 ++dot;
00917
00918 int n = atoi(std::string(i1->begin,dot).c_str());
00919
00920 iterator end = i1->end;
00921
00922 if( end - dot > 4)
00923 end = dot + 4;
00924
00925 ++dot;
00926
00927 int f = 0;
00928
00929 int multiplicator = 100;
00930 while( dot != end) {
00931 f += (*dot - 48)*multiplicator;
00932 multiplicator /= 10;
00933 ++dot;
00934 }
00935
00936 return expression_ptr(new decimal_expression(n, f));
00937 } else if(i1->type == TOKEN_STRING_LITERAL) {
00938 return expression_ptr(new string_expression(std::string(i1->begin+1,i1->end-1)));
00939 }
00940 } else if(i1->type == TOKEN_IDENTIFIER &&
00941 (i1+1)->type == TOKEN_LPARENS &&
00942 (i2-1)->type == TOKEN_RPARENS) {
00943 const token* begin = i1, *end = i2;
00944 int nleft = 0, nright = 0;
00945 for(const token* i = i1; i != i2; ++i) {
00946 if(i->type == TOKEN_LPARENS) {
00947 ++nleft;
00948 } else if(i->type == TOKEN_RPARENS) {
00949 ++nright;
00950 }
00951 }
00952
00953 if(nleft == nright) {
00954 std::vector<expression_ptr> args;
00955 parse_args(i1+2,i2-1,&args,symbols);
00956 try{
00957 return expression_ptr( create_function(std::string(i1->begin,i1->end),args,symbols) );
00958 }
00959 catch(formula_error& e) {
00960 throw formula_error(e.type, tokens_to_string(begin,end), *i1->filename, i1->line_number);
00961 }
00962 }
00963 }
00964
00965 throw formula_error("Could not parse expression", tokens_to_string(i1, i2), *i1->filename, i1->line_number);
00966 }
00967 if (op + 1 == end) {
00968 throw formula_error("Expected another token", tokens_to_string(begin,end-1), *op->filename, op->line_number);
00969 }
00970 if(op == i1) {
00971 try{
00972 return expression_ptr(new unary_operator_expression(
00973 std::string(op->begin,op->end),
00974 parse_expression(op+1,i2,symbols)));
00975 }
00976 catch(formula_error& e) {
00977 throw formula_error( e.type, tokens_to_string(begin,end-1), *op->filename, op->line_number);
00978 }
00979 }
00980
00981 const std::string op_name(op->begin,op->end);
00982
00983 if(op_name == ".") {
00984 return expression_ptr(new dot_expression(
00985 parse_expression(i1,op,symbols),
00986 parse_expression(op+1,i2,symbols)));
00987 }
00988
00989 if(op_name == "where") {
00990 expr_table_ptr table(new expr_table());
00991 parse_where_clauses(op+1, i2, table, symbols);
00992 return expression_ptr(new where_expression(parse_expression(i1, op, symbols),
00993 table));
00994 }
00995
00996 return expression_ptr(new operator_expression(
00997 op_name, parse_expression(i1,op,symbols),
00998 parse_expression(op+1,i2,symbols)));
00999 }
01000
01001 }
01002
01003 formula_ptr formula::create_optional_formula(const std::string& str, function_symbol_table* symbols)
01004 {
01005 if(str.empty()) {
01006 return formula_ptr();
01007 }
01008
01009 return formula_ptr(new formula(str, symbols));
01010 }
01011
01012 formula::formula(const std::string& str, function_symbol_table* symbols) :
01013 expr_(),
01014 str_(str)
01015 {
01016 using namespace formula_tokenizer;
01017
01018 std::vector<token> tokens;
01019 std::string::const_iterator i1 = str.begin(), i2 = str.end();
01020
01021
01022 bool fai_keyword = false;
01023
01024 std::vector< std::pair< std::string, int> > files;
01025
01026 std::set< std::string > filenames;
01027 files.push_back( std::make_pair( "formula", 1 ) );
01028 filenames.insert( "formula" );
01029 std::set< std::string >::iterator filenames_it = filenames.begin();
01030
01031 while(i1 != i2) {
01032 try {
01033
01034 tokens.push_back( get_token(i1,i2) );
01035
01036 TOKEN_TYPE current_type = tokens.back().type;
01037
01038 if(current_type == TOKEN_WHITESPACE) {
01039 tokens.pop_back();
01040 } else
01041 if( current_type == TOKEN_COMMENT) {
01042
01043 int counter = 0;
01044 std::string comment = std::string(tokens.back().begin,tokens.back().end);
01045 for( std::string::iterator str_it = comment.begin(); str_it != comment.end(); ++str_it)
01046 if( *str_it == '\n' )
01047 counter++;
01048
01049 files.back().second+=counter;
01050 tokens.pop_back();
01051 } else
01052 if( current_type == TOKEN_EOL) {
01053 files.back().second++;
01054 tokens.pop_back();
01055 } else
01056 if( ( current_type == TOKEN_KEYWORD) && ( std::string(tokens.back().begin,tokens.back().end) == "fai") ) {
01057 fai_keyword = true;
01058 tokens.pop_back();
01059 } else
01060 if( ( current_type == TOKEN_KEYWORD) && ( std::string(tokens.back().begin,tokens.back().end) == "faiend") ) {
01061 if (files.size() > 1) {
01062 files.pop_back();
01063 filenames_it = filenames.find( files.back().first );
01064 tokens.pop_back();
01065 } else {
01066 throw formula_error("Unexpected 'faiend' found", "", "", 0);
01067 }
01068 } else
01069 if (fai_keyword) {
01070 if(current_type == TOKEN_STRING_LITERAL) {
01071 std::string str = std::string(tokens.back().begin,tokens.back().end);
01072 files.push_back( std::make_pair( str , 1 ) );
01073 std::pair < std::set< std::string>::iterator , bool > ret;
01074 ret = filenames.insert( str );
01075 if(ret.second==true) {
01076 filenames_it = ret.first;
01077 } else {
01078 throw formula_error("Faifile already included", "fai" + str, "", 0);
01079 }
01080 tokens.pop_back();
01081 fai_keyword = false;
01082 } else {
01083 throw formula_error("Expected string after the 'fai'", "fai", "", 0);
01084 }
01085 } else {
01086
01087 tokens.back().filename = &(*filenames_it);
01088 tokens.back().line_number = files.back().second;
01089 }
01090 } catch(token_error& e) {
01091
01092 std::string str = "";
01093 if(!tokens.empty()) {
01094 token* tok_it = &tokens[0] + tokens.size()-1;
01095 while( ( tok_it != &tokens[0] ) && (tok_it->line_number == tokens.back().line_number) )
01096 --tok_it;
01097
01098 if( tok_it != &tokens[0] && tok_it != &tokens[0] + tokens.size() -1)
01099 ++tok_it;
01100
01101 str = tokens_to_string( tok_it, &tokens[0] + tokens.size() );
01102 }
01103
01104 throw formula_error(e.description_, str + e.formula_, *filenames_it, files.back().second);
01105 }
01106 }
01107
01108 if(files.size() > 1) {
01109 throw formula_error("Missing 'faiend', make sure each .fai file ends with it", "", "", 0);
01110 }
01111
01112 if(!tokens.empty()) {
01113 expr_ = parse_expression(&tokens[0],&tokens[0] + tokens.size(), symbols);
01114 } else {
01115 expr_ = expression_ptr(new null_expression());
01116 }
01117 }
01118 formula::formula(const token* i1, const token* i2, function_symbol_table* symbols) :
01119 expr_(),
01120 str_()
01121 {
01122
01123 if(i1 != i2) {
01124 expr_ = parse_expression(i1,i2, symbols);
01125 } else {
01126 expr_ = expression_ptr(new null_expression());
01127 }
01128 }
01129
01130 variant formula::execute(const formula_callable& variables, formula_debugger *fdb) const
01131 {
01132 try {
01133 return expr_->evaluate(variables, fdb);
01134 } catch(type_error& e) {
01135 std::cerr << "formula type error: " << e.message << "\n";
01136 return variant();
01137 }
01138 }
01139
01140 variant formula::execute(formula_debugger *fdb) const
01141 {
01142 static map_formula_callable null_callable;
01143 return execute(null_callable,fdb);
01144 }
01145
01146 formula_error::formula_error(const std::string& type, const std::string& formula,
01147 const std::string& file, int line)
01148 : error()
01149 , type(type)
01150 , formula(formula)
01151 , filename(file)
01152 , line(line)
01153 {
01154 std::stringstream ss;
01155 ss << "Formula error in " << filename << ":" << line
01156 << "\nIn formula " << formula
01157 << "\nError: " << type;
01158 message = ss.str();
01159 }
01160
01161 }
01162
01163 #ifdef UNIT_TEST_FORMULA
01164 using namespace game_logic;
01165 class mock_char : public formula_callable {
01166 variant get_value(const std::string& key) const {
01167 if(key == "strength") {
01168 return variant(15);
01169 } else if(key == "agility") {
01170 return variant(12);
01171 }
01172
01173 return variant(10);
01174 }
01175 };
01176 class mock_party : public formula_callable {
01177 variant get_value(const std::string& key) const {
01178 if(key == "members") {
01179 i_[0].add("strength",variant(12));
01180 i_[1].add("strength",variant(16));
01181 i_[2].add("strength",variant(14));
01182 std::vector<variant> members;
01183 for(int n = 0; n != 3; ++n) {
01184 members.push_back(variant(&i_[n]));
01185 }
01186
01187 return variant(&members);
01188 } else if(key == "char") {
01189 return variant(&c_);
01190 } else {
01191 return variant(0);
01192 }
01193 }
01194
01195 mock_char c_;
01196 mutable map_formula_callable i_[3];
01197
01198 };
01199
01200 #include <time.h>
01201
01202 int main()
01203 {
01204 srand(time(NULL));
01205 try {
01206 mock_char c;
01207 mock_party p;
01208
01209 assert(formula("strength").execute(c).as_int() == 15);
01210 assert(formula("17").execute(c).as_int() == 17);
01211 assert(formula("strength/2 + agility").execute(c).as_int() == 19);
01212 assert(formula("(strength+agility)/2").execute(c).as_int() == 13);
01213 assert(formula("strength > 12").execute(c).as_int() == 1);
01214 assert(formula("strength > 18").execute(c).as_int() == 0);
01215 assert(formula("if(strength > 12, 7, 2)").execute(c).as_int() == 7);
01216 assert(formula("if(strength > 18, 7, 2)").execute(c).as_int() == 2);
01217 assert(formula("2 and 1").execute(c).as_int() == 1);
01218 assert(formula("2 and 0").execute(c).as_int() == 0);
01219 assert(formula("2 or 0").execute(c).as_int() == 1);
01220 assert(formula("-5").execute(c).as_int() == -5);
01221 assert(formula("not 5").execute(c).as_int() == 0);
01222 assert(formula("not 0").execute(c).as_int() == 1);
01223 assert(formula("abs(5)").execute(c).as_int() == 5);
01224 assert(formula("abs(-5)").execute(c).as_int() == 5);
01225 assert(formula("min(3,5)").execute(c).as_int() == 3);
01226 assert(formula("min(5,2)").execute(c).as_int() == 2);
01227 assert(formula("max(3,5)").execute(c).as_int() == 5);
01228 assert(formula("max(5,2)").execute(c).as_int() == 5);
01229 assert(formula("max(4,5,[2,18,7])").execute(c).as_int() == 18);
01230 assert(formula("char.strength").execute(p).as_int() == 15);
01231 assert(formula("choose(members,strength).strength").execute(p).as_int() == 16);
01232 assert(formula("4^2").execute().as_int() == 16);
01233 assert(formula("2+3^3").execute().as_int() == 29);
01234 assert(formula("2*3^3+2").execute().as_int() == 56);
01235 assert(formula("9^3").execute().as_int() == 729);
01236 assert(formula("x*5 where x=1").execute().as_int() == 5);
01237 assert(formula("x*(a*b where a=2,b=1) where x=5").execute().as_int() == 10);
01238 assert(formula("char.strength * ability where ability=3").execute(p).as_int() == 45);
01239 assert(formula("'abcd' = 'abcd'").execute(p).as_bool() == true);
01240 assert(formula("'abcd' = 'acd'").execute(p).as_bool() == false);
01241 assert(formula("'strength, agility: {strength}, {agility}'").execute(c).as_string() ==
01242 "strength, agility: 15, 12");
01243 const int dice_roll = formula("3d6").execute().as_int();
01244 assert(dice_roll >= 3 && dice_roll <= 18);
01245
01246 variant myarray = formula("[1,2,3]").execute();
01247 assert(myarray.num_elements() == 3);
01248 assert(myarray[0].as_int() == 1);
01249 assert(myarray[1].as_int() == 2);
01250 assert(myarray[2].as_int() == 3);
01251 } catch(formula_error& e) {
01252 std::cerr << "parse error\n";
01253 }
01254 }
01255 #endif