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