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