The Battle for Wesnoth  1.19.0-dev
formula.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "formula/formula.hpp"
17 
18 #include "formula/callable.hpp"
19 #include "formula/function.hpp"
20 #include "formula/string_utils.hpp"
21 #include "random.hpp"
23 #include "log.hpp"
24 
25 #include <cassert>
26 #include <set>
27 #include <sstream>
28 
29 // This is here only for the below initialization code.
30 // If other logging is required in this file, it should use a different logdomain
31 // (probably "scripting/formula" or something)
32 static lg::log_domain log_engine("engine");
33 #define ERR_NG LOG_STREAM(err, log_engine)
34 
35 namespace utils {
36  namespace detail {
37  std::string evaluate_formula_impl(const std::string&);
38 
39  std::string evaluate_formula_impl(const std::string& formula) {
40  try {
41  const wfl::formula form(formula);
42  return form.evaluate().string_cast();
43  } catch(const wfl::formula_error& e) {
44  ERR_NG << "Formula in WML string cannot be evaluated due to "
45  << e.type << "\n\t--> \"";
46  return "";
47  }
48  }
49 
50  struct formula_initer {
53  }
54  } init;
55  }
56 }
57 
58 namespace wfl
59 {
60 using expr_table = std::map<std::string, expression_ptr>;
61 using expr_table_evaluated = std::map<std::string, variant>;
62 using expr_table_ptr = std::shared_ptr<expr_table>;
63 
64 // Function used when creating error reports.
65 // Parses all tokens passed to parse_expression, thus there are no EOL or whitespaces
66 static std::string tokens_to_string(const tk::token* i1, const tk::token* i2)
67 {
68  std::ostringstream expr;
69  while(i1 != i2) {
70  expr << std::string(i1->begin, i1->end) << " ";
71  ++i1;
72  }
73 
74  return expr.str();
75 }
76 
78 {
79 public:
81 
82  std::string str() const
83  {
84  return "";
85  }
86 
87 private:
88  variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
89  {
90  return variant();
91  }
92 };
93 
94 // Implemented further down
95 expression_ptr parse_expression(const tk::token* i1, const tk::token* i2, function_symbol_table* symbols);
96 
97 
98 const char* const formula::id_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
99 
100 formula::formula(const std::string& text, function_symbol_table* symbols)
101  : expr_()
102  , str_(text)
103  , managed_symbols_(symbols ? nullptr : new function_symbol_table)
104  , symbols_(symbols ? symbols : managed_symbols_.get())
105 {
106  std::vector<tk::token> tokens;
107  std::string::const_iterator i1 = text.begin(), i2 = text.end();
108 
109  // Set true when 'fai' keyword is found
110  bool fai_keyword = false;
111 
112  // Set true when 'wfl' keyword is found
113  bool wfl_keyword = false;
114 
115  // Used to locally keep the track of which file we parse actually and in which line we are
116  std::vector<std::pair<std::string, int>> files;
117 
118  // Used as a source of strings - we point to these strings from tokens
119  std::set<std::string> filenames;
120 
121  files.emplace_back("formula", 1);
122  filenames.insert("formula");
123 
124  std::set<std::string>::iterator filenames_it = filenames.begin();
125 
126  while(i1 != i2) {
127  try {
128  tokens.push_back(tk::get_token(i1,i2));
129 
130  tk::token_type current_type = tokens.back().type;
131 
132  if(current_type == tk::token_type::whitespace) {
133  tokens.pop_back();
134  } else if(current_type == tk::token_type::comment) {
135  // Since we can have multiline comments, let's see how many EOL are within it
136  int counter = 0;
137 
138  std::string comment = std::string(tokens.back().begin, tokens.back().end);
139  for(const auto& str_it : comment) {
140  if(str_it == '\n') {
141  counter++;
142  }
143  }
144 
145  files.back().second += counter;
146  tokens.pop_back();
147  } else if(current_type == tk::token_type::eol) {
148  files.back().second++;
149  tokens.pop_back();
150  } else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) == "fai")) {
151  fai_keyword = true;
152  tokens.pop_back();
153  } else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) == "wfl")) {
154  wfl_keyword = true;
155  tokens.pop_back();
156  } else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) == "faiend")) {
157  if(files.size() > 1) {
158  files.pop_back();
159  filenames_it = filenames.find(files.back().first);
160 
161  tokens.pop_back();
162  } else {
163  throw formula_error("Unexpected 'faiend' found", "", "", 0);
164  }
165  } else if((current_type == tk::token_type::keyword) && (std::string(tokens.back().begin, tokens.back().end) == "wflend")) {
166  if(files.size() > 1) {
167  files.pop_back();
168  filenames_it = filenames.find(files.back().first);
169 
170  tokens.pop_back();
171  } else {
172  throw formula_error("Unexpected 'wflend' found", "", "", 0);
173  }
174  } else if(fai_keyword || wfl_keyword) {
175  if(current_type == tk::token_type::string_literal) {
176  std::string str = std::string(tokens.back().begin, tokens.back().end);
177  files.emplace_back(str , 1);
178 
179  auto [pos, success] = filenames.insert(str);
180 
181  if(success) {
182  filenames_it = pos;
183  } else {
184  if(fai_keyword) {
185  throw formula_error("Faifile already included", "fai" + str, "", 0);
186  } else {
187  throw formula_error("Wflfile already included", "wfl" + str, "", 0);
188  }
189  }
190 
191  tokens.pop_back();
192  fai_keyword = false;
193  wfl_keyword = false;
194  } else {
195  if(fai_keyword) {
196  throw formula_error("Expected string after the 'fai'", "fai", "", 0);
197  } else {
198  throw formula_error("Expected string after the 'wfl'", "wfl", "", 0);
199  }
200  }
201  } else {
202  // In every token not specified above, store line number and name of file it came from
203  tokens.back().filename = &(*filenames_it);
204  tokens.back().line_number = files.back().second;
205  }
206  } catch(const tk::token_error& e) {
207  // When we catch token error, we should write whole line in which error occurred,
208  // so we merge info from token and everything we had in the line so far
209  std::string str = "";
210  if(!tokens.empty()) {
211  tk::token* tok_it = &tokens[0] + tokens.size()-1;
212  while(( tok_it != &tokens[0] ) && (tok_it->line_number == tokens.back().line_number)) {
213  --tok_it;
214  }
215 
216  if(tok_it != &tokens[0] && tok_it != &tokens[0] + tokens.size() -1) {
217  ++tok_it;
218  }
219 
220  str = tokens_to_string(tok_it, &tokens[0] + tokens.size());
221  }
222 
223  throw formula_error(e.description_, str + e.formula_, *filenames_it, files.back().second);
224  }
225  }
226 
227  if(files.size() > 1) {
228  throw formula_error("Missing 'wflend', make sure each .wfl file ends with it", "", "", 0);
229  }
230 
231  if(!tokens.empty()) {
232  expr_ = parse_expression(&tokens[0], &tokens[0] + tokens.size(), symbols_);
233  } else {
234  expr_ = std::make_shared<null_expression>();
235  }
236 }
237 
239  : expr_()
240  , str_()
241  , managed_symbols_(symbols ? nullptr : new function_symbol_table)
242  , symbols_(symbols ? symbols : managed_symbols_.get())
243 {
244  if(i1 != i2) {
245  expr_ = parse_expression(i1, i2, symbols);
246  } else {
247  expr_ = std::make_shared<null_expression>();
248  }
249 }
250 
252 {
253  if(str.empty()) {
254  return formula_ptr();
255  }
256 
257  return formula_ptr(new formula(str, symbols));
258 }
259 
261 {
262  try {
263  return expr_->evaluate(variables, fdb);
264  } catch(const type_error& e) {
265  PLAIN_LOG << "formula type error: " << e.message;
266  return variant();
267  }
268 }
269 
271 {
272  static map_formula_callable null_callable;
273  return execute(null_callable,fdb);
274 }
275 
276 
277 formula_error::formula_error(const std::string& type, const std::string& formula,
278  const std::string& file, int line)
279  : error()
280  , type(type)
281  , formula(formula)
282  , filename(file)
283  , line(line)
284 {
285  std::stringstream ss;
286  ss << "Formula error in " << filename << ":" << line
287  << "\nIn formula " << formula
288  << "\nError: " << type;
289  message = ss.str();
290 }
291 
292 
293 /**
294  * Classes that encapsulate and handle the various formula functions.
295  */
297 {
298 public:
300  : symbols_(symbols)
301  {}
302 
303  virtual std::string str() const
304  {
305  return "{function_list_expression()}";
306  }
307 
308 private:
309  variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
310  {
311  std::vector<variant> res;
312  for(const std::string& fcn_name : symbols_->get_function_names()) {
313  res.emplace_back(fcn_name);
314  }
315 
316  return variant(res);
317  }
318 
320 };
321 
323 {
324 public:
325  explicit list_expression(const std::vector<expression_ptr>& items)
326  : items_(items)
327  {}
328 
329 private:
330  variant execute(const formula_callable& variables, formula_debugger*fdb) const
331  {
332  std::vector<variant> res;
333  res.reserve(items_.size());
334  for(const auto& i : items_) {
335  res.push_back(i->evaluate(variables, add_debug_info(fdb, 0, "[list element]")));
336  }
337 
338  return variant(res);
339  }
340 
341  std::vector<expression_ptr> items_;
342 
343  std::string str() const
344  {
345  std::stringstream s;
346  s << '[';
347  bool first_item = true;
348  for(expression_ptr a : items_) {
349  if(!first_item) {
350  s << ',';
351  } else {
352  first_item = false;
353  }
354  s << a->str();
355  }
356  s << ']';
357  return s.str();
358  }
359 };
360 
362 {
363 public:
364  explicit map_expression(const std::vector<expression_ptr>& items)
365  : items_(items)
366  {}
367 
368  virtual std::string str() const
369  {
370  std::stringstream s;
371  s << " [";
372  for(std::vector<expression_ptr>::const_iterator i = items_.begin(); (i != items_.end()) && (i + 1 != items_.end()) ; i += 2) {
373  if(i != items_.begin()) {
374  s << ", ";
375  }
376  s << (*i)->str();
377  s << " -> ";
378  s << (*(i+1))->str();
379  }
380  if(items_.empty()) {
381  s << "->";
382  }
383  s << " ]";
384  return s.str();
385  }
386 
387 private:
388  variant execute(const formula_callable& variables, formula_debugger*fdb) const
389  {
390  std::map<variant,variant> res;
391  for(std::vector<expression_ptr>::const_iterator i = items_.begin(); (i != items_.end()) && (i + 1 != items_.end()) ; i += 2) {
392  variant key = (*i)->evaluate(variables, add_debug_info(fdb, 0, "key ->"));
393  variant value = (*(i+1))->evaluate(variables, add_debug_info(fdb, 1, "-> value"));
394  res[key] = value;
395  }
396 
397  return variant(res);
398  }
399 
400  std::vector<expression_ptr> items_;
401 };
402 
404 {
405 public:
406  unary_operator_expression(const std::string& op, expression_ptr arg)
407  : op_(),op_str_(op)
408  , operand_(arg)
409  {
410  if(op == "not") {
411  op_ = NOT;
412  } else if(op == "-") {
413  op_ = SUB;
414  } else {
415  throw formula_error("Illegal unary operator: '" + op + "'" , "", "", 0);
416  }
417  }
418 
419  virtual std::string str() const
420  {
421  std::stringstream s;
422  s << op_str_ << '('<< operand_->str() << ')';
423  return s.str();
424  }
425 
426 private:
427  variant execute(const formula_callable& variables, formula_debugger*fdb) const
428  {
429  const variant res = operand_->evaluate(variables, add_debug_info(fdb, 0, op_str_ + " unary"));
430  switch(op_) {
431  case NOT:
432  return res.as_bool() ? variant(0) : variant(1);
433  case SUB:
434  default:
435  return -res;
436  }
437  }
438 
439  enum OP { NOT, SUB };
441  std::string op_str_;
443 };
444 
446 {
447 public:
448  explicit string_callable(const variant& string) : string_(string) {}
449 
451  {
452  add_input(inputs, "size");
453  add_input(inputs, "empty");
454  add_input(inputs, "char");
455  add_input(inputs, "word");
456  add_input(inputs, "item");
457  }
458 
459  variant get_value(const std::string& key) const
460  {
461  if(key == "size") {
462  return variant(string_.as_string().length());
463  } else if(key == "empty") {
464  return variant(string_.as_string().empty());
465  } else if(key == "char" || key == "chars") {
466  std::vector<variant> chars;
467  for(char c : string_.as_string()) {
468  chars.emplace_back(std::string(1, c));
469  }
470 
471  return variant(chars);
472  } else if(key == "word" || key == "words") {
473  std::vector<variant> words;
474  const std::string& str = string_.as_string();
475  std::size_t next_space = 0;
476  do {
477  std::size_t last_space = next_space;
478  next_space = str.find_first_of(" \t", next_space);
479  words.emplace_back(str.substr(last_space, next_space - last_space));
480  next_space = str.find_first_not_of(" \t", next_space);
481  } while(next_space != std::string::npos);
482 
483  return variant(words);
484  } else if(key == "item" || key == "items") {
485  std::vector<std::string> split = utils::parenthetical_split(string_.as_string(), ',');
486  std::vector<variant> items;
487  items.reserve(split.size());
488  for(const std::string& s : split) {
489  items.emplace_back(s);
490  }
491 
492  return variant(items);
493  }
494 
495  return variant();
496  }
497 
498 private:
500 };
501 
503 {
504 public:
505  explicit list_callable(const variant& list) : list_(list) {}
506 
508  {
513  }
514 
515  variant get_value(const std::string& key) const
516  {
517  if(key == "size") {
518  return variant(list_.num_elements());
519  } else if(key == "empty") {
520  return variant(list_.num_elements() == 0);
521  } else if(key == "first") {
522  if(list_.num_elements() > 0) {
523  return list_[0];
524  }
525 
526  return variant();
527  } else if(key == "last") {
528  if(list_.num_elements() > 0) {
529  return list_[list_.num_elements()-1];
530  }
531 
532  return variant();
533  }
534 
535  return variant();
536  }
537 
538 private:
540 };
541 
543 {
544 public:
545  explicit map_callable(const variant& map) : map_(map) {}
546 
548  {
551 
552  for(const auto& v : map_) {
553  // variant_iterator does not implement operator->,
554  // and to do so is notrivial since it returns temporaries for maps.
555  const variant& key_variant = v.get_member("key");
556  if(!key_variant.is_string()) {
557  continue;
558  }
559 
560  std::string key = key_variant.as_string();
561  bool valid = true;
562  for(char c : key) {
563  if(!isalpha(c) && c != '_') {
564  valid = false;
565  break;
566  }
567  }
568 
569  if(valid) {
570  add_input(inputs, key);
571  }
572  }
573  }
574 
575  variant get_value(const std::string& key) const
576  {
577  const variant key_variant(key);
578  if(map_.as_map().find(key_variant) != map_.as_map().end()) {
579  return map_[key_variant];
580  } else if(key == "size") {
581  return variant(map_.num_elements());
582  } else if(key == "empty") {
583  return variant(map_.num_elements() == 0);
584  }
585 
586  return variant();
587  }
588 
589 private:
591 };
592 
594 {
595 public:
596  dot_callable(const formula_callable &global, const formula_callable& local)
597  : global_(global), local_(local)
598  {}
599 
600 private:
602 
604  {
605  return local_.get_inputs(inputs);
606  }
607 
608  variant get_value(const std::string& key) const
609  {
610  variant v = local_.query_value(key);
611 
612  if( v == variant() )
613  return global_.query_value(key);
614  else
615  return v;
616  }
617 };
618 
620 {
621 public:
623  : left_(left), right_(right)
624  {}
625 
626  std::string str() const
627  {
628  std::stringstream s;
629  s << left_->str() << "." << right_->str();
630  return s.str();
631  }
632 
633 private:
634  variant execute(const formula_callable& variables, formula_debugger*fdb) const
635  {
636  const variant left = left_->evaluate(variables, add_debug_info(fdb,0,"left ."));
637  if(!left.is_callable()) {
638  if(left.is_list()) {
639  list_callable list_call(left);
640  dot_callable callable(variables, list_call);
641  return right_->evaluate(callable,fdb);
642  }
643 
644  if(left.is_map()) {
645  map_callable map_call(left);
646  dot_callable callable(variables, map_call);
647  return right_->evaluate(callable,fdb);
648  }
649 
650  if(left.is_string()) {
651  string_callable string_call(left);
652  dot_callable callable(variables, string_call);
653  return right_->evaluate(callable,fdb);
654  }
655 
656  return left;
657  }
658 
659  dot_callable callable(variables, *left.as_callable());
660  return right_->evaluate(callable, add_debug_info(fdb,1,". right"));
661  }
662 
664 };
665 
667 {
668 public:
670  : left_(left), key_(key)
671  {}
672 
673  std::string str() const
674  {
675  std::stringstream s;
676  s << left_->str() << '[' << key_->str() << ']';
677  return s.str();
678  }
679 
680 private:
681  variant execute(const formula_callable& variables, formula_debugger*fdb) const
682  {
683  const variant left = left_->evaluate(variables, add_debug_info(fdb,0,"base[]"));
684  const variant key = key_->evaluate(variables, add_debug_info(fdb,1,"[index]"));
685  if(left.is_list() || left.is_map()) {
686  return left[key];
687  }
688 
689  return variant();
690  }
691 
693 };
694 
696 {
697 public:
698  operator_expression(const std::string& op, expression_ptr left, expression_ptr right)
699  : op_(OP(op[0])), op_str_(op), left_(left), right_(right)
700  {
701  if(op == ">=") {
702  op_ = GTE;
703  } else if(op == "<=") {
704  op_ = LTE;
705  } else if(op == "!=") {
706  op_ = NEQ;
707  } else if(op == "and") {
708  op_ = AND;
709  } else if(op == "or") {
710  op_ = OR;
711  } else if(op == ".+") {
712  op_ = ADDL;
713  } else if(op == ".-") {
714  op_ = SUBL;
715  } else if(op == ".*") {
716  op_ = MULL;
717  } else if(op == "./") {
718  op_ = DIVL;
719  } else if(op == "..") {
720  op_ = OP_CAT;
721  } else if(op == "in") {
722  op_ = OP_IN;
723  }
724  }
725 
726  std::string str() const
727  {
728  std::stringstream s;
729  s << '(' << left_->str() << op_str_ << right_->str() << ')';
730  return s.str();
731  }
732 
733 private:
734  variant execute(const formula_callable& variables, formula_debugger*fdb) const
735  {
736  const variant left = left_->evaluate(variables, add_debug_info(fdb, 0, "left " + op_str_));
737  const variant right = right_->evaluate(variables, add_debug_info(fdb, 1, op_str_ + " right"));
738 
739  switch(op_) {
740  case AND:
741  return left.as_bool() == false ? left : right;
742  case OR:
743  return left.as_bool() ? left : right;
744  case ADD:
745  return left + right;
746  case SUB:
747  return left - right;
748  case MUL:
749  return left * right;
750  case DIV:
751  return left / right;
752  case POW:
753  return left ^ right;
754  case ADDL:
755  return left.list_elements_add(right);
756  case SUBL:
757  return left.list_elements_sub(right);
758  case MULL:
759  return left.list_elements_mul(right);
760  case DIVL:
761  return left.list_elements_div(right);
762  case OP_IN:
763  return variant(right.contains(left));
764  case OP_CAT:
765  return left.concatenate(right);
766  case EQ:
767  return left == right ? variant(1) : variant(0);
768  case NEQ:
769  return left != right ? variant(1) : variant(0);
770  case LTE:
771  return left <= right ? variant(1) : variant(0);
772  case GTE:
773  return left >= right ? variant(1) : variant(0);
774  case LT:
775  return left < right ? variant(1) : variant(0);
776  case GT:
777  return left > right ? variant(1) : variant(0);
778  case MOD:
779  return left % right;
780  case RAN:
781  return left.build_range(right);
782  case DICE:
783  return variant(dice_roll(left.as_int(), right.as_int()));
784  default:
785  PLAIN_LOG << "ERROR: Unimplemented operator!";
786  return variant();
787  }
788  }
789 
790  static int dice_roll(int num_rolls, int faces)
791  {
792  int res = 0;
793  while(faces > 0 && num_rolls-- > 0) {
794  res += randomness::generator->get_random_int(1, faces);
795  }
796 
797  return res;
798  }
799 
800  //In some cases a IN or CAT macros are defined.
801  enum OP { AND, OR, NEQ, LTE, GTE, OP_CAT, OP_IN, GT='>', LT='<', EQ='=', RAN='~',
802  ADD='+', SUB='-', MUL='*', DIV='/', ADDL, SUBL, MULL, DIVL, DICE='d', POW='^', MOD='%' };
803 
805  std::string op_str_;
807 };
808 
810 {
811 public:
813  : formula_callable(false)
814  , base_(base)
815  , table_(table)
816  , evaluated_table_()
817  , debugger_(fdb)
818  {
819  }
820 
821 private:
826 
828  {
829  for(expr_table::const_iterator i = table_->begin(); i != table_->end(); ++i) {
830  add_input(inputs, i->first);
831  }
832  }
833 
834  variant get_value(const std::string& key) const
835  {
836  expr_table::iterator i = table_->find(key);
837  if(i != table_->end()) {
838  expr_table_evaluated::const_iterator ev = evaluated_table_.find(key);
839  if(ev != evaluated_table_.end()) {
840  return ev->second;
841  }
842 
843  variant v = i->second->evaluate(base_, add_debug_info(debugger_, 0, "where[" + key + "]"));
844  evaluated_table_[key] = v;
845  return v;
846  }
847 
848  return base_.query_value(key);
849  }
850 };
851 
853 {
854 public:
856  : body_(body), clauses_(clauses)
857  {}
858 
859  std::string str() const
860  {
861  std::stringstream s;
862  s << "{where:(";
863  s << body_->str();
864  for(const expr_table::value_type &a : *clauses_) {
865  s << ", [" << a.first << "] -> ["<< a.second->str()<<"]";
866  }
867  s << ")}";
868  return s.str();
869  }
870 
871 private:
874 
875  variant execute(const formula_callable& variables,formula_debugger*fdb) const
876  {
877  where_variables wrapped_variables(variables, clauses_, fdb);
878  return body_->evaluate(wrapped_variables, add_debug_info(fdb, 0, "... where"));
879  }
880 };
881 
882 
884 {
885 public:
886  explicit identifier_expression(const std::string& id) : id_(id) {}
887 
888  std::string str() const
889  {
890  return id_;
891  }
892 
893 private:
894  variant execute(const formula_callable& variables, formula_debugger* /*fdb*/) const
895  {
896  return variables.query_value(id_);
897  }
898 
899  std::string id_;
900 };
901 
903 {
904 public:
905  explicit integer_expression(int i) : i_(i) {}
906 
907  std::string str() const
908  {
909  std::stringstream s;
910  s << i_;
911  return s.str();
912  }
913 
914 private:
915  variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
916  {
917  return variant(i_);
918  }
919 
920  int i_;
921 };
922 
924 {
925 public:
926  decimal_expression(int i, int f) : i_(i), f_(f) {}
927 
928  std::string str() const
929  {
930  std::stringstream s;
931  s << i_ << '.';
932  s.width(3);
933  s.fill('0');
934  s << f_;
935  return s.str();
936  }
937 
938 private:
939  variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
940  {
941  return variant(i_ * 1000 + f_, variant::DECIMAL_VARIANT );
942  }
943 
944  int i_, f_;
945 };
946 
948 {
949 public:
950  explicit string_expression(std::string str)
951  : str_()
952  , subs_()
953  {
954  std::string::iterator i = str.begin();
955  while((i = std::find(i, str.end(), '[')) != str.end()) {
956  int bracket_depth = 0;
957  std::string::iterator j = i + 1;
958  while(j != str.end() && (bracket_depth > 0 || *j != ']')) {
959  if(*j == '[') {
960  bracket_depth++;
961  } else if(*j == ']' && bracket_depth > 0) {
962  bracket_depth--;
963  }
964  ++j;
965  }
966 
967  if(j == str.end()) {
968  break;
969  }
970 
971  const std::string formula_str(i+1, j);
972  const int pos = std::distance(str.begin(), i);
973  if(j - i == 2 && (i[1] == '(' || i[1] == '\'' || i[1] == ')')) {
974  // Bracket contained nothing but a quote or parenthesis.
975  // This means it was intended as a literal quote or square bracket.
976  i = str.erase(i);
977  if(*i == '(') {
978  *i = '[';
979  } else if(*i == ')') {
980  *i = ']';
981  }
982 
983  i = str.erase(i + 1);
984  continue;
985  } else {
986  i = str.erase(i, j+1);
987  }
988 
989  substitution sub;
990  sub.pos = pos;
991  try {
992  sub.calculation.reset(new formula(formula_str));
993  } catch(formula_error& e) {
994  e.filename += " - string substitution";
995  throw;
996  }
997 
998  subs_.push_back(sub);
999  }
1000 
1001  std::reverse(subs_.begin(), subs_.end());
1002 
1003  str_ = variant(str);
1004  }
1005 
1006  std::string str() const
1007  {
1008  std::string res = str_.as_string();
1009  int j = res.size() - 1;
1010 
1011  for(const auto& sub : subs_) {
1012  for(; j >= sub.pos && j >= 0; j--) {
1013  if(res[j] == '\'') {
1014  res.replace(j, 1, "[']");
1015  } else if(res[j] == '[') {
1016  res.replace(j, 1, "[(]");
1017  } else if(res[j] == ']') {
1018  res.replace(j, 1, "[)]");
1019  }
1020  }
1021 
1022  const std::string str = "[" + sub.calculation->str() + "]";
1023  res.insert(sub.pos, str);
1024  }
1025 
1026  for(; j >= 0; j--) {
1027  if(res[j] == '\'') {
1028  res.replace(j, 1, "[']");
1029  } else if(res[j] == '[') {
1030  res.replace(j, 1, "[(]");
1031  } else if(res[j] == ']') {
1032  res.replace(j, 1, "[)]");
1033  }
1034  }
1035 
1036  return "'" + res + "'";
1037  }
1038 
1039 private:
1040  variant execute(const formula_callable& variables, formula_debugger*fdb) const
1041  {
1042  if(subs_.empty()) {
1043  return str_;
1044  }
1045 
1046  std::string res = str_.as_string();
1047  for(std::size_t i = 0; i < subs_.size(); ++i) {
1048  const int j = subs_.size() - i - 1;
1049  const substitution& sub = subs_[i];
1050  add_debug_info(fdb, j, "[string subst]");
1051  const std::string str = sub.calculation->evaluate(variables,fdb).string_cast();
1052  res.insert(sub.pos, str);
1053  }
1054 
1055  return variant(res);
1056  }
1057 
1059  {
1061 
1062  int pos;
1064  };
1065 
1067  std::vector<substitution> subs_;
1068 };
1069 
1070 
1071 /**
1072  * Functions to handle the actual parsing of WFL.
1073  */
1074 static int operator_precedence(const tk::token& t)
1075 {
1076  static std::map<std::string,int> precedence_map;
1077  if(precedence_map.empty()) {
1078  int n = 0;
1079  precedence_map["not"] = ++n;
1080  precedence_map["where"] = ++n;
1081  precedence_map["or"] = ++n;
1082  precedence_map["and"] = ++n;
1083  precedence_map["="] = ++n;
1084  precedence_map["!="] = n;
1085  precedence_map["<"] = n;
1086  precedence_map[">"] = n;
1087  precedence_map["<="] = n;
1088  precedence_map[">="] = n;
1089  precedence_map["in"] = n;
1090  precedence_map["~"] = ++n;
1091  precedence_map["+"] = ++n;
1092  precedence_map["-"] = n;
1093  precedence_map[".."] = n;
1094  precedence_map["*"] = ++n;
1095  precedence_map["/"] = n;
1096  precedence_map["%"] = ++n;
1097  precedence_map["^"] = ++n;
1098  precedence_map["d"] = ++n;
1099  precedence_map["."] = ++n;
1100  }
1101 
1102  assert(precedence_map.count(std::string(t.begin, t.end)));
1103  return precedence_map[std::string(t.begin, t.end)];
1104 }
1105 
1106 static void parse_function_args(const tk::token* &i1, const tk::token* i2, std::vector<std::string>* res)
1107 {
1108  const tk::token* begin = i1, *end = i2; // These are used for error reporting
1109 
1110  if(i1->type == tk::token_type::lparens) {
1111  ++i1;
1112  } else {
1113  throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1114  }
1115 
1116  while((i1-> type != tk::token_type::rparens) && (i1 != i2)) {
1117  if(i1->type == tk::token_type::identifier) {
1118  if(std::string((i1+1)->begin, (i1+1)->end) == "*") {
1119  res->push_back(std::string(i1->begin, i1->end) + std::string("*"));
1120  ++i1;
1121  } else {
1122  res->push_back(std::string(i1->begin, i1->end));
1123  }
1124  } else if(i1->type == tk::token_type::comma) {
1125  //do nothing
1126  } else {
1127  throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1128  }
1129 
1130  ++i1;
1131  }
1132 
1133  if(i1->type != tk::token_type::rparens) {
1134  throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1135  }
1136 
1137  ++i1;
1138 }
1139 
1140 static void parse_args(const tk::token* i1, const tk::token* i2,
1141  std::vector<expression_ptr>* res, function_symbol_table* symbols)
1142 {
1143  int parens = 0;
1144  const tk::token* beg = i1;
1145  while(i1 != i2) {
1146  if(i1->type == tk::token_type::lparens || i1->type == tk::token_type::lsquare ) {
1147  ++parens;
1148  } else if(i1->type == tk::token_type::rparens || i1->type == tk::token_type::rsquare ) {
1149  --parens;
1150  } else if(i1->type == tk::token_type::comma && !parens) {
1151  res->push_back(parse_expression(beg, i1, symbols));
1152  beg = i1+1;
1153  }
1154 
1155  ++i1;
1156  }
1157 
1158  if(beg != i1) {
1159  res->push_back(parse_expression(beg, i1, symbols));
1160  }
1161 }
1162 
1163 static void parse_set_args(const tk::token* i1, const tk::token* i2,
1164  std::vector<expression_ptr>* res, function_symbol_table* symbols)
1165 {
1166  int parens = 0;
1167  bool check_pointer = false;
1168  const tk::token* beg = i1;
1169  const tk::token* begin = i1, *end = i2; // These are used for error reporting
1170  while(i1 != i2) {
1171  if(i1->type == tk::token_type::lparens || i1->type == tk::token_type::lsquare) {
1172  ++parens;
1173  } else if(i1->type == tk::token_type::rparens || i1->type == tk::token_type::rsquare) {
1174  --parens;
1175  } else if(i1->type == tk::token_type::pointer && !parens ) {
1176  if(!check_pointer) {
1177  check_pointer = true;
1178  res->push_back(parse_expression(beg, i1, symbols));
1179  beg = i1+1;
1180  } else {
1181  throw formula_error("Too many '->' operators found", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1182  }
1183  } else if(i1->type == tk::token_type::comma && !parens ) {
1184  if(check_pointer)
1185  check_pointer = false;
1186  else {
1187  throw formula_error("Expected comma, but '->' found", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1188  }
1189  res->push_back(parse_expression(beg, i1, symbols));
1190  beg = i1+1;
1191  }
1192 
1193  ++i1;
1194  }
1195 
1196  if(beg != i1) {
1197  res->push_back(parse_expression(beg, i1, symbols));
1198  }
1199 }
1200 
1201 static void parse_where_clauses(const tk::token* i1, const tk::token* i2, expr_table_ptr res, function_symbol_table* symbols)
1202 {
1203  int parens = 0;
1204  const tk::token* original_i1_cached = i1;
1205  const tk::token* beg = i1;
1206  const tk::token* begin = i1, *end = i2; // These are used for error reporting
1207  std::string var_name;
1208 
1209  while(i1 != i2) {
1210  if(i1->type == tk::token_type::lparens || i1->type == tk::token_type::lsquare) {
1211  ++parens;
1212  } else if(i1->type == tk::token_type::rparens || i1->type == tk::token_type::rsquare) {
1213  --parens;
1214  } else if(!parens) {
1215  if(i1->type == tk::token_type::comma) {
1216  if(var_name.empty()) {
1217  throw formula_error("There is 'where <expression>' but 'where name=<expression>' was needed",
1218  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1219  }
1220 
1221  (*res)[var_name] = parse_expression(beg, i1, symbols);
1222  beg = i1+1;
1223  var_name = "";
1224  } else if(i1->type == tk::token_type::operator_token) {
1225  std::string op_name(i1->begin, i1->end);
1226 
1227  if(op_name == "=") {
1228  if(beg->type != tk::token_type::identifier) {
1229  if(i1 == original_i1_cached) {
1230  throw formula_error("There is 'where <expression>' but 'where name=<expression>' was needed",
1231  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1232  } else {
1233  throw formula_error("There is 'where <expression>=<expression>' but 'where name=<expression>' was needed",
1234  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1235  }
1236  } else if(beg+1 != i1) {
1237  throw formula_error("There is 'where name <expression>=<expression>' but 'where name=<expression>' was needed",
1238  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1239  } else if(!var_name.empty()) {
1240  throw formula_error("There is 'where name=name=<expression>' but 'where name=<expression>' was needed",
1241  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1242  }
1243 
1244  var_name.insert(var_name.end(), beg->begin, beg->end);
1245  beg = i1+1;
1246  }
1247  }
1248  }
1249  ++i1;
1250  }
1251 
1252  if(beg != i1) {
1253  if(var_name.empty()) {
1254  throw formula_error("There is 'where <expression>' but 'where name=<expression>' was needed",
1255  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1256  }
1257 
1258  (*res)[var_name] = parse_expression(beg, i1, symbols);
1259  }
1260 }
1261 
1263 {
1264  if(i1 == i2) {
1265  throw formula_error("Empty expression", "", *i1->filename, i1->line_number);
1266  }
1267 
1268  std::unique_ptr<function_symbol_table> temp_functions;
1269  if(!symbols) {
1270  temp_functions.reset(new function_symbol_table(function_symbol_table::get_builtins()));
1271  symbols = temp_functions.get();
1272  }
1273 
1274  const tk::token* begin = i1, *end = i2; // These are used for error reporting
1275 
1276  if(i1->type == tk::token_type::keyword && (i1 + 1)->type == tk::token_type::identifier) {
1277  if(std::string(i1->begin, i1->end) == "def") {
1278  ++i1;
1279  const std::string formula_name = std::string(i1->begin, i1->end);
1280 
1281  std::vector<std::string> args;
1282  parse_function_args(++i1, i2, &args);
1283 
1284  const tk::token* beg = i1;
1285  while((i1 != i2) && (i1->type != tk::token_type::semicolon)) {
1286  ++i1;
1287  }
1288 
1289  const std::string precond = "";
1290  if(symbols == nullptr) {
1291  throw formula_error("Function symbol table required but not present", "",*i1->filename, i1->line_number);
1292  }
1293 
1294  symbols->add_function(formula_name,
1295  std::make_shared<user_formula_function>(
1296  formula_name, std::make_shared<const formula>(beg, i1, symbols),
1297  formula::create_optional_formula(precond, symbols), args
1298  )
1299  );
1300 
1301  if((i1 == i2) || (i1 == (i2-1))) {
1302  return std::make_shared<function_list_expression>(symbols);
1303  } else {
1304  return parse_expression((i1+1), i2, symbols);
1305  }
1306  }
1307  }
1308 
1309  int parens = 0;
1310  const tk::token* op = nullptr;
1311  bool operator_group = false;
1312 
1313  for(const tk::token* i = i1; i != i2; ++i) {
1314  if(i->type == tk::token_type::lparens || i->type == tk::token_type::lsquare) {
1315  ++parens;
1316  } else if(i->type == tk::token_type::rparens || i->type == tk::token_type::rsquare) {
1317  --parens;
1318  } else if(parens == 0 && i->type == tk::token_type::operator_token) {
1319  if((!operator_group ) && (op == nullptr || operator_precedence(*op) >= operator_precedence(*i))) {
1320  // Need special exception for exponentiation to be right-associative
1321  if(*i->begin != '^' || op == nullptr || *op->begin != '^') {
1322  op = i;
1323  }
1324  }
1325  operator_group = true;
1326  } else {
1327  operator_group = false;
1328  }
1329  }
1330 
1331  if(op == nullptr) {
1332  if(i1->type == tk::token_type::lparens && (i2-1)->type == tk::token_type::rparens) {
1333  return parse_expression(i1+1,i2-1,symbols);
1334  } else if((i2-1)->type == tk::token_type::rsquare) { //check if there is [ ] : either a list/map definition, or a operator
1335  // First, a special case for an empty map
1336  if(i2 - i1 == 3 && i1->type == tk::token_type::lsquare && (i1+1)->type == tk::token_type::pointer) {
1337  return std::make_shared<map_expression>(std::vector<expression_ptr>());
1338  }
1339 
1340  const tk::token* tok = i2-2;
1341  int square_parens = 0;
1342  bool is_map = false;
1343  while ((tok->type != tk::token_type::lsquare || square_parens) && tok != i1) {
1344  if(tok->type == tk::token_type::rsquare) {
1345  square_parens++;
1346  } else if(tok->type == tk::token_type::lsquare) {
1347  square_parens--;
1348  } else if((tok->type == tk::token_type::pointer) && !square_parens ) {
1349  is_map = true;
1350  }
1351  --tok;
1352  }
1353 
1354  if(tok->type == tk::token_type::lsquare) {
1355  if(tok == i1) {
1356  // Create a list or a map
1357  std::vector<expression_ptr> args;
1358 
1359  if( is_map ) {
1360  parse_set_args(i1+1, i2-1, &args, symbols);
1361  return std::make_shared<map_expression>(args);
1362  } else {
1363  parse_args(i1+1,i2-1,&args,symbols);
1364  return std::make_shared<list_expression>(args);
1365  }
1366  } else {
1367  // Execute operator [ ]
1368  try{
1369  return std::make_shared<square_bracket_expression>(
1370  parse_expression(i1, tok, symbols),
1371  parse_expression(tok + 1, i2 - 1, symbols)
1372  );
1373  } catch (const formula_error& e){
1374  throw formula_error( e.type, tokens_to_string(i1, i2-1), *i1->filename, i1->line_number );
1375  }
1376  }
1377  }
1378  } else if(i2 - i1 == 1) {
1379  if(i1->type == tk::token_type::keyword) {
1380  if(std::string(i1->begin, i1->end) == "functions") {
1381  return std::make_shared<function_list_expression>(symbols);
1382  }
1383  } else if(i1->type == tk::token_type::identifier) {
1384  return std::make_shared<identifier_expression>(std::string(i1->begin, i1->end));
1385  } else if(i1->type == tk::token_type::integer) {
1386  int n = std::stoi(std::string(i1->begin, i1->end));
1387  return std::make_shared<integer_expression>(n);
1388  } else if(i1->type == tk::token_type::decimal) {
1389  tk::iterator dot = i1->begin;
1390  while(*dot != '.') {
1391  ++dot;
1392  }
1393 
1394  int n = std::stoi(std::string(i1->begin,dot));
1395 
1396  tk::iterator literal_end = i1->end;
1397 
1398  if(literal_end - dot > 4) {
1399  literal_end = dot + 4;
1400  }
1401 
1402  ++dot;
1403 
1404  int f = 0;
1405 
1406  int multiplicator = 100;
1407  while(dot != literal_end) {
1408  f += (*dot - 48) * multiplicator;
1409  multiplicator /= 10;
1410  ++dot;
1411  }
1412 
1413  return std::make_shared<decimal_expression>(n, f);
1414  } else if(i1->type == tk::token_type::string_literal) {
1415  return std::make_shared<string_expression>(std::string(i1->begin + 1, i1->end - 1));
1416  }
1417  } else if(i1->type == tk::token_type::identifier &&
1418  (i1+1)->type == tk::token_type::lparens &&
1419  (i2-1)->type == tk::token_type::rparens)
1420  {
1421  const tk::token* function_call_begin = i1, *function_call_end = i2; // These are used for error reporting
1422  int nleft = 0, nright = 0;
1423  for(const tk::token* i = i1; i != i2; ++i) {
1424  if(i->type == tk::token_type::lparens) {
1425  ++nleft;
1426  } else if(i->type == tk::token_type::rparens) {
1427  ++nright;
1428  }
1429  }
1430 
1431  if(nleft == nright) {
1432  std::vector<expression_ptr> args;
1433  parse_args(i1+2,i2-1,&args,symbols);
1434  try{
1435  return symbols->create_function(std::string(i1->begin, i1->end),args);
1436  }
1437  catch(const formula_error& e) {
1438  throw formula_error(e.type, tokens_to_string(function_call_begin, function_call_end), *i1->filename, i1->line_number);
1439  }
1440  }
1441  }
1442 
1443  throw formula_error("Could not parse expression", tokens_to_string(i1, i2), *i1->filename, i1->line_number);
1444  }
1445 
1446  if(op + 1 == end) {
1447  throw formula_error("Expected another token", tokens_to_string(begin, end - 1), *op->filename, op->line_number);
1448  }
1449 
1450  if(op == i1) {
1451  try{
1452  return expression_ptr(
1453  new unary_operator_expression(std::string(op->begin, op->end), parse_expression(op + 1, i2 ,symbols)));
1454  } catch(const formula_error& e) {
1455  throw formula_error( e.type, tokens_to_string(begin,end - 1), *op->filename, op->line_number);
1456  }
1457  }
1458 
1459  const std::string op_name(op->begin,op->end);
1460 
1461  if(op_name == ".") {
1462  return expression_ptr(
1463  new dot_expression(
1464  parse_expression(i1, op, symbols),
1465  parse_expression(op + 1,i2, symbols)
1466  )
1467  );
1468  }
1469 
1470  if(op_name == "where") {
1471  expr_table_ptr table(new expr_table());
1472  parse_where_clauses(op+1, i2, table, symbols);
1473 
1474  return std::make_shared<where_expression>(parse_expression(i1, op, symbols), table);
1475  }
1476 
1477  return expression_ptr(
1478  new operator_expression(op_name,
1479  parse_expression(i1, op, symbols),
1480  parse_expression(op + 1, i2, symbols)
1481  )
1482  );
1483 }
1484 
1485 } // namespace wfl
double t
Definition: astarsearch.cpp:63
int get_random_int(int min, int max)
Definition: random.hpp:51
decimal_expression(int i, int f)
Definition: formula.cpp:926
std::string str() const
Definition: formula.cpp:928
variant execute(const formula_callable &, formula_debugger *) const
Definition: formula.cpp:939
void get_inputs(formula_input_vector &inputs) const
Definition: formula.cpp:603
variant get_value(const std::string &key) const
Definition: formula.cpp:608
const formula_callable & local_
Definition: formula.cpp:601
dot_callable(const formula_callable &global, const formula_callable &local)
Definition: formula.cpp:596
const formula_callable & global_
Definition: formula.cpp:601
expression_ptr left_
Definition: formula.cpp:663
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:634
std::string str() const
Definition: formula.cpp:626
dot_expression(expression_ptr left, expression_ptr right)
Definition: formula.cpp:622
expression_ptr right_
Definition: formula.cpp:663
variant query_value(const std::string &key) const
Definition: callable.hpp:50
formula_input_vector inputs() const
Definition: callable.hpp:63
static void add_input(formula_input_vector &inputs, const std::string &key, formula_access access_type=formula_access::read_only)
Definition: callable.hpp:136
virtual void get_inputs(formula_input_vector &) const
Definition: callable.hpp:70
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:40
expression_ptr expr_
Definition: formula.hpp:81
function_symbol_table * symbols_
Definition: formula.hpp:86
static formula_ptr create_optional_formula(const std::string &str, function_symbol_table *symbols=nullptr)
Definition: formula.cpp:251
formula(const std::string &str, function_symbol_table *symbols=nullptr)
Definition: formula.cpp:100
const std::string & str() const
Definition: formula.hpp:73
variant execute(const formula_callable &variables, formula_debugger *fdb=nullptr) const
Definition: formula.cpp:260
static const char *const id_chars
Definition: formula.hpp:75
Classes that encapsulate and handle the various formula functions.
Definition: formula.cpp:297
function_list_expression(function_symbol_table *symbols)
Definition: formula.cpp:299
variant execute(const formula_callable &, formula_debugger *) const
Definition: formula.cpp:309
virtual std::string str() const
Definition: formula.cpp:303
function_symbol_table * symbols_
Definition: formula.cpp:319
static std::shared_ptr< function_symbol_table > get_builtins()
Definition: function.cpp:1527
expression_ptr create_function(const std::string &fn, const std::vector< expression_ptr > &args) const
Definition: function.cpp:1495
void add_function(const std::string &name, formula_function_ptr &&fcn)
Definition: function.cpp:1490
std::set< std::string > get_function_names() const
Definition: function.cpp:1513
identifier_expression(const std::string &id)
Definition: formula.cpp:886
std::string str() const
Definition: formula.cpp:888
variant execute(const formula_callable &variables, formula_debugger *) const
Definition: formula.cpp:894
variant execute(const formula_callable &, formula_debugger *) const
Definition: formula.cpp:915
std::string str() const
Definition: formula.cpp:907
void get_inputs(formula_input_vector &inputs) const
Definition: formula.cpp:507
list_callable(const variant &list)
Definition: formula.cpp:505
variant get_value(const std::string &key) const
Definition: formula.cpp:515
std::string str() const
Definition: formula.cpp:343
list_expression(const std::vector< expression_ptr > &items)
Definition: formula.cpp:325
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:330
std::vector< expression_ptr > items_
Definition: formula.cpp:341
map_callable(const variant &map)
Definition: formula.cpp:545
variant get_value(const std::string &key) const
Definition: formula.cpp:575
void get_inputs(formula_input_vector &inputs) const
Definition: formula.cpp:547
std::vector< expression_ptr > items_
Definition: formula.cpp:400
map_expression(const std::vector< expression_ptr > &items)
Definition: formula.cpp:364
virtual std::string str() const
Definition: formula.cpp:368
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:388
std::string str() const
Definition: formula.cpp:82
variant execute(const formula_callable &, formula_debugger *) const
Definition: formula.cpp:88
expression_ptr right_
Definition: formula.cpp:806
expression_ptr left_
Definition: formula.cpp:806
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:734
std::string str() const
Definition: formula.cpp:726
static int dice_roll(int num_rolls, int faces)
Definition: formula.cpp:790
operator_expression(const std::string &op, expression_ptr left, expression_ptr right)
Definition: formula.cpp:698
square_bracket_expression(expression_ptr left, expression_ptr key)
Definition: formula.cpp:669
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:681
std::string str() const
Definition: formula.cpp:673
string_callable(const variant &string)
Definition: formula.cpp:448
void get_inputs(formula_input_vector &inputs) const
Definition: formula.cpp:450
variant get_value(const std::string &key) const
Definition: formula.cpp:459
string_expression(std::string str)
Definition: formula.cpp:950
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:1040
std::vector< substitution > subs_
Definition: formula.cpp:1067
std::string str() const
Definition: formula.cpp:1006
unary_operator_expression(const std::string &op, expression_ptr arg)
Definition: formula.cpp:406
virtual std::string str() const
Definition: formula.cpp:419
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:427
int as_int() const
Definition: variant.cpp:291
bool is_map() const
Definition: variant.hpp:68
bool is_list() const
Definition: variant.hpp:66
variant concatenate(const variant &v) const
Definition: variant.cpp:560
const_formula_callable_ptr as_callable() const
Definition: variant.hpp:83
std::size_t num_elements() const
Definition: variant.cpp:267
@ DECIMAL_VARIANT
Definition: variant.hpp:31
variant get_member(const std::string &name) const
Definition: variant.cpp:276
variant list_elements_mul(const variant &v) const
Definition: variant.cpp:548
variant list_elements_add(const variant &v) const
Definition: variant.cpp:536
const std::string & as_string() const
Definition: variant.cpp:318
std::string string_cast() const
Definition: variant.cpp:638
variant list_elements_div(const variant &v) const
Definition: variant.cpp:554
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:313
bool is_string() const
Definition: variant.hpp:67
variant build_range(const variant &v) const
Definition: variant.cpp:586
bool is_callable() const
Definition: variant.hpp:65
const std::map< variant, variant > & as_map() const
Definition: variant.cpp:330
variant list_elements_sub(const variant &v) const
Definition: variant.cpp:542
expression_ptr body_
Definition: formula.cpp:872
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:875
std::string str() const
Definition: formula.cpp:859
expr_table_ptr clauses_
Definition: formula.cpp:873
where_expression(expression_ptr body, expr_table_ptr clauses)
Definition: formula.cpp:855
const formula_callable & base_
Definition: formula.cpp:822
variant get_value(const std::string &key) const
Definition: formula.cpp:834
where_variables(const formula_callable &base, expr_table_ptr table, formula_debugger *fdb)
Definition: formula.cpp:812
void get_inputs(formula_input_vector &inputs) const
Definition: formula.cpp:827
expr_table_ptr table_
Definition: formula.cpp:823
expr_table_evaluated evaluated_table_
Definition: formula.cpp:824
formula_debugger * debugger_
Definition: formula.cpp:825
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: formula.cpp:33
std::size_t i
Definition: function.cpp:968
expression_ptr expr_
Definition: function.cpp:755
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:207
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:295
CURSOR_TYPE get()
Definition: cursor.cpp:216
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
Definition: draw.cpp:180
const std::vector< std::string > items
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:60
boost::variant< constant, n_var, boost::recursive_wrapper< not_op >, boost::recursive_wrapper< ternary_op > > expr
struct utils::detail::formula_initer init
std::string(* evaluate_formula)(const std::string &formula)
std::string evaluate_formula_impl(const std::string &)
Definition: formula.cpp:39
std::vector< std::string > parenthetical_split(std::string_view val, const char separator, std::string_view left, std::string_view right, const int flags)
Splits a string based either on a separator, except then the text appears within specified parenthesi...
std::vector< std::string > split(const config_attribute_value &val)
token get_token(iterator &i1, const iterator i2)
Definition: tokenizer.cpp:44
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Definition: contexts.hpp:43
formula_debugger * add_debug_info(formula_debugger *fdb, int arg_number, const std::string &f_name)
std::map< std::string, expression_ptr > expr_table
Definition: formula.cpp:60
static void parse_function_args(const tk::token *&i1, const tk::token *i2, std::vector< std::string > *res)
Definition: formula.cpp:1106
std::vector< formula_input > formula_input_vector
std::shared_ptr< const formula > const_formula_ptr
Definition: formula_fwd.hpp:24
std::shared_ptr< formula_expression > expression_ptr
Definition: formula.hpp:29
static int operator_precedence(const tk::token &t)
Functions to handle the actual parsing of WFL.
Definition: formula.cpp:1074
std::shared_ptr< expr_table > expr_table_ptr
Definition: formula.cpp:62
std::shared_ptr< formula > formula_ptr
Definition: formula_fwd.hpp:22
static void parse_where_clauses(const tk::token *i1, const tk::token *i2, expr_table_ptr res, function_symbol_table *symbols)
Definition: formula.cpp:1201
static void parse_set_args(const tk::token *i1, const tk::token *i2, std::vector< expression_ptr > *res, function_symbol_table *symbols)
Definition: formula.cpp:1163
std::map< std::string, variant > expr_table_evaluated
Definition: formula.cpp:61
expression_ptr parse_expression(const tk::token *i1, const tk::token *i2, function_symbol_table *symbols)
Definition: formula.cpp:1262
static std::string tokens_to_string(const tk::token *i1, const tk::token *i2)
Definition: formula.cpp:66
static void parse_args(const tk::token *i1, const tk::token *i2, std::vector< expression_ptr > *res, function_symbol_table *symbols)
Definition: formula.cpp:1140
std::string message
Definition: exceptions.hpp:30
std::string type
Definition: formula.hpp:106
std::string filename
Definition: formula.hpp:108
const std::string * filename
Definition: tokenizer.hpp:69
mock_char c
static map_location::DIRECTION n
static map_location::DIRECTION s
#define e
#define f
#define a