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