The Battle for Wesnoth  1.15.10+dev
formula.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "formula/formula.hpp"
16 
17 #include "formula/callable.hpp"
18 #include "formula/function.hpp"
19 #include "formula/string_utils.hpp"
20 #include "random.hpp"
22 #include "log.hpp"
23 
24 #include <cassert>
25 #include <set>
26 #include <sstream>
27 
28 // This is here only for the below initialization code.
29 // If other logging is required in this file, it should use a different logdomain
30 // (probably "scripting/formula" or something)
31 static lg::log_domain log_engine("engine");
32 #define ERR_NG LOG_STREAM(err, log_engine)
33 
34 namespace utils {
35  namespace detail {
36  std::string evaluate_formula_impl(const std::string&);
37 
38  std::string evaluate_formula_impl(const std::string& formula) {
39  try {
40  const wfl::formula form(formula);
41  return form.evaluate().string_cast();
42  } catch(const wfl::formula_error& e) {
43  ERR_NG << "Formula in WML string cannot be evaluated due to "
44  << e.type << "\n\t--> \"";
45  return "";
46  }
47  }
48 
49  struct formula_initer {
52  }
53  } init;
54  }
55 }
56 
57 namespace wfl
58 {
59 using expr_table = std::map<std::string, expression_ptr>;
60 using expr_table_evaluated = std::map<std::string, variant>;
61 using expr_table_ptr = std::shared_ptr<expr_table>;
62 
63 // Function used when creating error reports.
64 // Parses all tokens passed to parse_expression, thus there are no EOL or whitespaces
65 static std::string tokens_to_string(const tk::token* i1, const tk::token* i2)
66 {
67  std::ostringstream expr;
68  while(i1 != i2) {
69  expr << std::string(i1->begin, i1->end) << " ";
70  ++i1;
71  }
72 
73  return expr.str();
74 }
75 
77 {
78 public:
80 
81  std::string str() const
82  {
83  return "";
84  }
85 
86 private:
87  variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
88  {
89  return variant();
90  }
91 };
92 
93 // Implemented further down
95 
96 
97 const char* const formula::id_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
98 
99 formula::formula(const std::string& text, function_symbol_table* symbols)
100  : expr_()
101  , str_(text)
102  , managed_symbols_(symbols ? nullptr : new function_symbol_table)
103  , symbols_(symbols ? symbols : managed_symbols_.get())
104 {
105  std::vector<tk::token> tokens;
106  std::string::const_iterator i1 = text.begin(), i2 = text.end();
107 
108  // Set true when 'fai' keyword is found
109  bool fai_keyword = false;
110 
111  // Set true when 'wfl' keyword is found
112  bool wfl_keyword = false;
113 
114  // Used to locally keep the track of which file we parse actually and in which line we are
115  std::vector<std::pair<std::string, int>> files;
116 
117  // Used as a source of strings - we point to these strings from tokens
118  std::set<std::string> filenames;
119 
120  files.emplace_back("formula", 1);
121  filenames.insert("formula");
122 
123  std::set<std::string>::iterator filenames_it = filenames.begin();
124 
125  while(i1 != i2) {
126  try {
127  tokens.push_back(tk::get_token(i1,i2));
128 
129  tk::TOKEN_TYPE current_type = tokens.back().type;
130 
131  if(current_type == tk::TOKEN_WHITESPACE) {
132  tokens.pop_back();
133  } else if(current_type == tk::TOKEN_COMMENT) {
134  // Since we can have multiline comments, let's see how many EOL are within it
135  int counter = 0;
136 
137  std::string comment = std::string(tokens.back().begin, tokens.back().end);
138  for(const auto& str_it : comment) {
139  if(str_it == '\n') {
140  counter++;
141  }
142  }
143 
144  files.back().second += counter;
145  tokens.pop_back();
146  } else if(current_type == tk::TOKEN_EOL) {
147  files.back().second++;
148  tokens.pop_back();
149  } else if((current_type == tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) == "fai")) {
150  fai_keyword = true;
151  tokens.pop_back();
152  } else if((current_type == tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) == "wfl")) {
153  wfl_keyword = true;
154  tokens.pop_back();
155  } else if((current_type == tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) == "faiend")) {
156  if(files.size() > 1) {
157  files.pop_back();
158  filenames_it = filenames.find(files.back().first);
159 
160  tokens.pop_back();
161  } else {
162  throw formula_error("Unexpected 'faiend' found", "", "", 0);
163  }
164  } else if((current_type == tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) == "wflend")) {
165  if(files.size() > 1) {
166  files.pop_back();
167  filenames_it = filenames.find(files.back().first);
168 
169  tokens.pop_back();
170  } else {
171  throw formula_error("Unexpected 'wflend' found", "", "", 0);
172  }
173  } else if(fai_keyword || wfl_keyword) {
174  if(current_type == tk::TOKEN_STRING_LITERAL) {
175  std::string str = std::string(tokens.back().begin, tokens.back().end);
176  files.emplace_back(str , 1);
177 
178  auto [pos, success] = filenames.insert(str);
179 
180  if(success) {
181  filenames_it = pos;
182  } else {
183  if(fai_keyword) {
184  throw formula_error("Faifile already included", "fai" + str, "", 0);
185  } else {
186  throw formula_error("Wflfile already included", "wfl" + str, "", 0);
187  }
188  }
189 
190  tokens.pop_back();
191  fai_keyword = false;
192  wfl_keyword = false;
193  } else {
194  if(fai_keyword) {
195  throw formula_error("Expected string after the 'fai'", "fai", "", 0);
196  } else {
197  throw formula_error("Expected string after the 'wfl'", "wfl", "", 0);
198  }
199  }
200  } else {
201  // In every token not specified above, store line number and name of file it came from
202  tokens.back().filename = &(*filenames_it);
203  tokens.back().line_number = files.back().second;
204  }
205  } catch(const tk::token_error& e) {
206  // When we catch token error, we should write whole line in which error occurred,
207  // so we merge info from token and everything we had in the line so far
208  std::string str = "";
209  if(!tokens.empty()) {
210  tk::token* tok_it = &tokens[0] + tokens.size()-1;
211  while(( tok_it != &tokens[0] ) && (tok_it->line_number == tokens.back().line_number)) {
212  --tok_it;
213  }
214 
215  if(tok_it != &tokens[0] && tok_it != &tokens[0] + tokens.size() -1) {
216  ++tok_it;
217  }
218 
219  str = tokens_to_string(tok_it, &tokens[0] + tokens.size());
220  }
221 
222  throw formula_error(e.description_, str + e.formula_, *filenames_it, files.back().second);
223  }
224  }
225 
226  if(files.size() > 1) {
227  throw formula_error("Missing 'wflend', make sure each .wfl file ends with it", "", "", 0);
228  }
229 
230  if(!tokens.empty()) {
231  expr_ = parse_expression(&tokens[0], &tokens[0] + tokens.size(), symbols_);
232  } else {
233  expr_ = std::make_shared<null_expression>();
234  }
235 }
236 
238  : expr_()
239  , str_()
240  , managed_symbols_(symbols ? nullptr : new function_symbol_table)
241  , symbols_(symbols ? symbols : managed_symbols_.get())
242 {
243  if(i1 != i2) {
244  expr_ = parse_expression(i1, i2, symbols);
245  } else {
246  expr_ = std::make_shared<null_expression>();
247  }
248 }
249 
251 {
252  if(str.empty()) {
253  return formula_ptr();
254  }
255 
256  return formula_ptr(new formula(str, symbols));
257 }
258 
260 {
261  try {
262  return expr_->evaluate(variables, fdb);
263  } catch(const type_error& e) {
264  std::cerr << "formula type error: " << e.message << "\n";
265  return variant();
266  }
267 }
268 
270 {
271  static map_formula_callable null_callable;
272  return execute(null_callable,fdb);
273 }
274 
275 
276 formula_error::formula_error(const std::string& type, const std::string& formula,
277  const std::string& file, int line)
278  : error()
279  , type(type)
280  , formula(formula)
281  , filename(file)
282  , line(line)
283 {
284  std::stringstream ss;
285  ss << "Formula error in " << filename << ":" << line
286  << "\nIn formula " << formula
287  << "\nError: " << type;
288  message = ss.str();
289 }
290 
291 
292 /**
293  * Classes that encapsulate and handle the various formula functions.
294  */
296 {
297 public:
299  : symbols_(symbols)
300  {}
301 
302  virtual std::string str() const
303  {
304  return "{function_list_expression()}";
305  }
306 
307 private:
308  variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
309  {
310  std::vector<variant> res;
311  for(const std::string& fcn_name : symbols_->get_function_names()) {
312  res.emplace_back(fcn_name);
313  }
314 
315  return variant(res);
316  }
317 
319 };
320 
322 {
323 public:
324  explicit list_expression(const std::vector<expression_ptr>& items)
325  : items_(items)
326  {}
327 
328 private:
329  variant execute(const formula_callable& variables, formula_debugger*fdb) const
330  {
331  std::vector<variant> res;
332  res.reserve(items_.size());
333  for(const auto& i : items_) {
334  res.push_back(i->evaluate(variables, add_debug_info(fdb, 0, "[list element]")));
335  }
336 
337  return variant(res);
338  }
339 
340  std::vector<expression_ptr> items_;
341 
342  std::string str() const
343  {
344  std::stringstream s;
345  s << '[';
346  bool first_item = true;
347  for(expression_ptr a : items_) {
348  if(!first_item) {
349  s << ',';
350  } else {
351  first_item = false;
352  }
353  s << a->str();
354  }
355  s << ']';
356  return s.str();
357  }
358 };
359 
361 {
362 public:
363  explicit map_expression(const std::vector<expression_ptr>& items)
364  : items_(items)
365  {}
366 
367  virtual std::string str() const
368  {
369  std::stringstream s;
370  s << " [";
371  for(std::vector<expression_ptr>::const_iterator i = items_.begin(); (i != items_.end()) && (i + 1 != items_.end()) ; i += 2) {
372  if(i != items_.begin()) {
373  s << ", ";
374  }
375  s << (*i)->str();
376  s << " -> ";
377  s << (*(i+1))->str();
378  }
379  if(items_.empty()) {
380  s << "->";
381  }
382  s << " ]";
383  return s.str();
384  }
385 
386 private:
387  variant execute(const formula_callable& variables, formula_debugger*fdb) const
388  {
389  std::map<variant,variant> res;
390  for(std::vector<expression_ptr>::const_iterator i = items_.begin(); (i != items_.end()) && (i + 1 != items_.end()) ; i += 2) {
391  variant key = (*i)->evaluate(variables, add_debug_info(fdb, 0, "key ->"));
392  variant value = (*(i+1))->evaluate(variables, add_debug_info(fdb, 1, "-> value"));
393  res[key] = value;
394  }
395 
396  return variant(res);
397  }
398 
399  std::vector<expression_ptr> items_;
400 };
401 
403 {
404 public:
405  unary_operator_expression(const std::string& op, expression_ptr arg)
406  : op_(),op_str_(op)
407  , operand_(arg)
408  {
409  if(op == "not") {
410  op_ = NOT;
411  } else if(op == "-") {
412  op_ = SUB;
413  } else {
414  throw formula_error("Illegal unary operator: '" + op + "'" , "", "", 0);
415  }
416  }
417 
418  virtual std::string str() const
419  {
420  std::stringstream s;
421  s << op_str_ << '('<< operand_->str() << ')';
422  return s.str();
423  }
424 
425 private:
426  variant execute(const formula_callable& variables, formula_debugger*fdb) const
427  {
428  const variant res = operand_->evaluate(variables, add_debug_info(fdb, 0, op_str_ + " unary"));
429  switch(op_) {
430  case NOT:
431  return res.as_bool() ? variant(0) : variant(1);
432  case SUB:
433  default:
434  return -res;
435  }
436  }
437 
438  enum OP { NOT, SUB };
440  std::string op_str_;
442 };
443 
445 {
446 public:
447  explicit string_callable(const variant& string) : string_(string) {}
448 
449  void get_inputs(formula_input_vector& inputs) const
450  {
451  add_input(inputs, "size");
452  add_input(inputs, "empty");
453  add_input(inputs, "char");
454  add_input(inputs, "word");
455  add_input(inputs, "item");
456  }
457 
458  variant get_value(const std::string& key) const
459  {
460  if(key == "size") {
461  return variant(string_.as_string().length());
462  } else if(key == "empty") {
463  return variant(string_.as_string().empty());
464  } else if(key == "char" || key == "chars") {
465  std::vector<variant> chars;
466  for(char c : string_.as_string()) {
467  chars.emplace_back(std::string(1, c));
468  }
469 
470  return variant(chars);
471  } else if(key == "word" || key == "words") {
472  std::vector<variant> words;
473  const std::string& str = string_.as_string();
474  std::size_t next_space = 0;
475  do {
476  std::size_t last_space = next_space;
477  next_space = str.find_first_of(" \t", next_space);
478  words.emplace_back(str.substr(last_space, next_space - last_space));
479  next_space = str.find_first_not_of(" \t", next_space);
480  } while(next_space != std::string::npos);
481 
482  return variant(words);
483  } else if(key == "item" || key == "items") {
484  std::vector<std::string> split = utils::parenthetical_split(string_.as_string(), ',');
485  std::vector<variant> items;
486  items.reserve(split.size());
487  for(const std::string& s : split) {
488  items.emplace_back(s);
489  }
490 
491  return variant(items);
492  }
493 
494  return variant();
495  }
496 
497 private:
499 };
500 
502 {
503 public:
504  explicit list_callable(const variant& list) : list_(list) {}
505 
506  void get_inputs(formula_input_vector& inputs) const
507  {
508  add_input(inputs, "size", FORMULA_READ_WRITE);
509  add_input(inputs, "empty", FORMULA_READ_WRITE);
510  add_input(inputs, "first", FORMULA_READ_WRITE);
511  add_input(inputs, "last", FORMULA_READ_WRITE);
512  }
513 
514  variant get_value(const std::string& key) const
515  {
516  if(key == "size") {
517  return variant(list_.num_elements());
518  } else if(key == "empty") {
519  return variant(list_.num_elements() == 0);
520  } else if(key == "first") {
521  if(list_.num_elements() > 0) {
522  return list_[0];
523  }
524 
525  return variant();
526  } else if(key == "last") {
527  if(list_.num_elements() > 0) {
528  return list_[list_.num_elements()-1];
529  }
530 
531  return variant();
532  }
533 
534  return variant();
535  }
536 
537 private:
539 };
540 
542 {
543 public:
544  explicit map_callable(const variant& map) : map_(map) {}
545 
546  void get_inputs(formula_input_vector& inputs) const
547  {
548  add_input(inputs, "size", FORMULA_READ_WRITE);
549  add_input(inputs, "empty", FORMULA_READ_WRITE);
550 
551  for(const auto& v : map_) {
552  // variant_iterator does not implement operator->,
553  // and to do so is notrivial since it returns temporaries for maps.
554  const variant& key_variant = v.get_member("key");
555  if(!key_variant.is_string()) {
556  continue;
557  }
558 
559  std::string key = key_variant.as_string();
560  bool valid = true;
561  for(char c : key) {
562  if(!isalpha(c) && c != '_') {
563  valid = false;
564  break;
565  }
566  }
567 
568  if(valid) {
569  add_input(inputs, key);
570  }
571  }
572  }
573 
574  variant get_value(const std::string& key) const
575  {
576  const variant key_variant(key);
577  if(map_.as_map().find(key_variant) != map_.as_map().end()) {
578  return map_[key_variant];
579  } else if(key == "size") {
580  return variant(map_.num_elements());
581  } else if(key == "empty") {
582  return variant(map_.num_elements() == 0);
583  }
584 
585  return variant();
586  }
587 
588 private:
590 };
591 
593 {
594 public:
595  dot_callable(const formula_callable &global, const formula_callable& local)
596  : global_(global), local_(local)
597  {}
598 
599 private:
600  const formula_callable& global_, &local_;
601 
602  void get_inputs(formula_input_vector& inputs) const
603  {
604  return local_.get_inputs(inputs);
605  }
606 
607  variant get_value(const std::string& key) const
608  {
609  variant v = local_.query_value(key);
610 
611  if( v == variant() )
612  return global_.query_value(key);
613  else
614  return v;
615  }
616 };
617 
619 {
620 public:
622  : left_(left), right_(right)
623  {}
624 
625  std::string str() const
626  {
627  std::stringstream s;
628  s << left_->str() << "." << right_->str();
629  return s.str();
630  }
631 
632 private:
633  variant execute(const formula_callable& variables, formula_debugger*fdb) const
634  {
635  const variant left = left_->evaluate(variables, add_debug_info(fdb,0,"left ."));
636  if(!left.is_callable()) {
637  if(left.is_list()) {
638  list_callable list_call(left);
639  dot_callable callable(variables, list_call);
640  return right_->evaluate(callable,fdb);
641  }
642 
643  if(left.is_map()) {
644  map_callable map_call(left);
645  dot_callable callable(variables, map_call);
646  return right_->evaluate(callable,fdb);
647  }
648 
649  if(left.is_string()) {
650  string_callable string_call(left);
651  dot_callable callable(variables, string_call);
652  return right_->evaluate(callable,fdb);
653  }
654 
655  return left;
656  }
657 
658  dot_callable callable(variables, *left.as_callable());
659  return right_->evaluate(callable, add_debug_info(fdb,1,". right"));
660  }
661 
663 };
664 
666 {
667 public:
669  : left_(left), key_(key)
670  {}
671 
672  std::string str() const
673  {
674  std::stringstream s;
675  s << left_->str() << '[' << key_->str() << ']';
676  return s.str();
677  }
678 
679 private:
680  variant execute(const formula_callable& variables, formula_debugger*fdb) const
681  {
682  const variant left = left_->evaluate(variables, add_debug_info(fdb,0,"base[]"));
683  const variant key = key_->evaluate(variables, add_debug_info(fdb,1,"[index]"));
684  if(left.is_list() || left.is_map()) {
685  return left[key];
686  }
687 
688  return variant();
689  }
690 
692 };
693 
695 {
696 public:
698  : op_(OP(op[0])), op_str_(op), left_(left), right_(right)
699  {
700  if(op == ">=") {
701  op_ = GTE;
702  } else if(op == "<=") {
703  op_ = LTE;
704  } else if(op == "!=") {
705  op_ = NEQ;
706  } else if(op == "and") {
707  op_ = AND;
708  } else if(op == "or") {
709  op_ = OR;
710  } else if(op == ".+") {
711  op_ = ADDL;
712  } else if(op == ".-") {
713  op_ = SUBL;
714  } else if(op == ".*") {
715  op_ = MULL;
716  } else if(op == "./") {
717  op_ = DIVL;
718  } else if(op == "..") {
719  op_ = OP_CAT;
720  } else if(op == "in") {
721  op_ = OP_IN;
722  }
723  }
724 
725  std::string str() const
726  {
727  std::stringstream s;
728  s << '(' << left_->str() << op_str_ << right_->str() << ')';
729  return s.str();
730  }
731 
732 private:
733  variant execute(const formula_callable& variables, formula_debugger*fdb) const
734  {
735  const variant left = left_->evaluate(variables, add_debug_info(fdb, 0, "left " + op_str_));
736  const variant right = right_->evaluate(variables, add_debug_info(fdb, 1, op_str_ + " right"));
737 
738  switch(op_) {
739  case AND:
740  return left.as_bool() == false ? left : right;
741  case OR:
742  return left.as_bool() ? left : right;
743  case ADD:
744  return left + right;
745  case SUB:
746  return left - right;
747  case MUL:
748  return left * right;
749  case DIV:
750  return left / right;
751  case POW:
752  return left ^ right;
753  case ADDL:
754  return left.list_elements_add(right);
755  case SUBL:
756  return left.list_elements_sub(right);
757  case MULL:
758  return left.list_elements_mul(right);
759  case DIVL:
760  return left.list_elements_div(right);
761  case OP_IN:
762  return variant(right.contains(left));
763  case OP_CAT:
764  return left.concatenate(right);
765  case EQ:
766  return left == right ? variant(1) : variant(0);
767  case NEQ:
768  return left != right ? variant(1) : variant(0);
769  case LTE:
770  return left <= right ? variant(1) : variant(0);
771  case GTE:
772  return left >= right ? variant(1) : variant(0);
773  case LT:
774  return left < right ? variant(1) : variant(0);
775  case GT:
776  return left > right ? variant(1) : variant(0);
777  case MOD:
778  return left % right;
779  case RAN:
780  return left.build_range(right);
781  case DICE:
782  return variant(dice_roll(left.as_int(), right.as_int()));
783  default:
784  std::cerr << "ERROR: Unimplemented operator!" << std::endl;
785  return variant();
786  }
787  }
788 
789  static int dice_roll(int num_rolls, int faces)
790  {
791  int res = 0;
792  while(faces > 0 && num_rolls-- > 0) {
793  res += randomness::generator->get_random_int(1, faces);
794  }
795 
796  return res;
797  }
798 
799  //In some cases a IN or CAT macros are defined.
800  enum OP { AND, OR, NEQ, LTE, GTE, OP_CAT, OP_IN, GT='>', LT='<', EQ='=', RAN='~',
801  ADD='+', SUB='-', MUL='*', DIV='/', ADDL, SUBL, MULL, DIVL, DICE='d', POW='^', MOD='%' };
802 
804  std::string op_str_;
806 };
807 
809 {
810 public:
812  : formula_callable(false)
813  , base_(base)
814  , table_(table)
815  , evaluated_table_()
816  , debugger_(fdb)
817  {
818  }
819 
820 private:
825 
826  void get_inputs(formula_input_vector& inputs) const
827  {
828  for(expr_table::const_iterator i = table_->begin(); i != table_->end(); ++i) {
829  add_input(inputs, i->first);
830  }
831  }
832 
833  variant get_value(const std::string& key) const
834  {
835  expr_table::iterator i = table_->find(key);
836  if(i != table_->end()) {
837  expr_table_evaluated::const_iterator ev = evaluated_table_.find(key);
838  if(ev != evaluated_table_.end()) {
839  return ev->second;
840  }
841 
842  variant v = i->second->evaluate(base_, add_debug_info(debugger_, 0, "where[" + key + "]"));
843  evaluated_table_[key] = v;
844  return v;
845  }
846 
847  return base_.query_value(key);
848  }
849 };
850 
852 {
853 public:
855  : body_(body), clauses_(clauses)
856  {}
857 
858  std::string str() const
859  {
860  std::stringstream s;
861  s << "{where:(";
862  s << body_->str();
863  for(const expr_table::value_type &a : *clauses_) {
864  s << ", [" << a.first << "] -> ["<< a.second->str()<<"]";
865  }
866  s << ")}";
867  return s.str();
868  }
869 
870 private:
873 
874  variant execute(const formula_callable& variables,formula_debugger*fdb) const
875  {
876  where_variables wrapped_variables(variables, clauses_, fdb);
877  return body_->evaluate(wrapped_variables, add_debug_info(fdb, 0, "... where"));
878  }
879 };
880 
881 
883 {
884 public:
885  explicit identifier_expression(const std::string& id) : id_(id) {}
886 
887  std::string str() const
888  {
889  return id_;
890  }
891 
892 private:
893  variant execute(const formula_callable& variables, formula_debugger* /*fdb*/) const
894  {
895  return variables.query_value(id_);
896  }
897 
898  std::string id_;
899 };
900 
902 {
903 public:
904  explicit integer_expression(int i) : i_(i) {}
905 
906  std::string str() const
907  {
908  std::stringstream s;
909  s << i_;
910  return s.str();
911  }
912 
913 private:
914  variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
915  {
916  return variant(i_);
917  }
918 
919  int i_;
920 };
921 
923 {
924 public:
925  decimal_expression(int i, int f) : i_(i), f_(f) {}
926 
927  std::string str() const
928  {
929  std::stringstream s;
930  s << i_ << '.';
931  s.width(3);
932  s.fill('0');
933  s << f_;
934  return s.str();
935  }
936 
937 private:
938  variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
939  {
940  return variant(i_ * 1000 + f_, variant::DECIMAL_VARIANT );
941  }
942 
943  int i_, f_;
944 };
945 
947 {
948 public:
949  explicit string_expression(std::string str)
950  : str_()
951  , subs_()
952  {
953  std::string::iterator i = str.begin();
954  while((i = std::find(i, str.end(), '[')) != str.end()) {
955  int bracket_depth = 0;
956  std::string::iterator j = i + 1;
957  while(j != str.end() && (bracket_depth > 0 || *j != ']')) {
958  if(*j == '[') {
959  bracket_depth++;
960  } else if(*j == ']' && bracket_depth > 0) {
961  bracket_depth--;
962  }
963  ++j;
964  }
965 
966  if(j == str.end()) {
967  break;
968  }
969 
970  const std::string formula_str(i+1, j);
971  const int pos = std::distance(str.begin(), i);
972  if(j - i == 2 && (i[1] == '(' || i[1] == '\'' || i[1] == ')')) {
973  // Bracket contained nothing but a quote or parenthesis.
974  // This means it was intended as a literal quote or square bracket.
975  i = str.erase(i);
976  if(*i == '(') {
977  *i = '[';
978  } else if(*i == ')') {
979  *i = ']';
980  }
981 
982  i = str.erase(i + 1);
983  continue;
984  } else {
985  i = str.erase(i, j+1);
986  }
987 
988  substitution sub;
989  sub.pos = pos;
990  try {
991  sub.calculation.reset(new formula(formula_str));
992  } catch(formula_error& e) {
993  e.filename += " - string substitution";
994  throw;
995  }
996 
997  subs_.push_back(sub);
998  }
999 
1000  std::reverse(subs_.begin(), subs_.end());
1001 
1002  str_ = variant(str);
1003  }
1004 
1005  std::string str() const
1006  {
1007  std::string res = str_.as_string();
1008  int j = res.size() - 1;
1009 
1010  for(const auto& sub : subs_) {
1011  for(; j >= sub.pos && j >= 0; j--) {
1012  if(res[j] == '\'') {
1013  res.replace(j, 1, "[']");
1014  } else if(res[j] == '[') {
1015  res.replace(j, 1, "[(]");
1016  } else if(res[j] == ']') {
1017  res.replace(j, 1, "[)]");
1018  }
1019  }
1020 
1021  const std::string str = "[" + sub.calculation->str() + "]";
1022  res.insert(sub.pos, str);
1023  }
1024 
1025  for(; j >= 0; j--) {
1026  if(res[j] == '\'') {
1027  res.replace(j, 1, "[']");
1028  } else if(res[j] == '[') {
1029  res.replace(j, 1, "[(]");
1030  } else if(res[j] == ']') {
1031  res.replace(j, 1, "[)]");
1032  }
1033  }
1034 
1035  return "'" + res + "'";
1036  }
1037 
1038 private:
1039  variant execute(const formula_callable& variables, formula_debugger*fdb) const
1040  {
1041  if(subs_.empty()) {
1042  return str_;
1043  }
1044 
1045  std::string res = str_.as_string();
1046  for(std::size_t i = 0; i < subs_.size(); ++i) {
1047  const int j = subs_.size() - i - 1;
1048  const substitution& sub = subs_[i];
1049  add_debug_info(fdb, j, "[string subst]");
1050  const std::string str = sub.calculation->evaluate(variables,fdb).string_cast();
1051  res.insert(sub.pos, str);
1052  }
1053 
1054  return variant(res);
1055  }
1056 
1058  {
1059  substitution() : pos(0) , calculation() {}
1060 
1061  int pos;
1063  };
1064 
1066  std::vector<substitution> subs_;
1067 };
1068 
1069 
1070 /**
1071  * Functions to handle the actual parsing of WFL.
1072  */
1073 static int operator_precedence(const tk::token& t)
1074 {
1075  static std::map<std::string,int> precedence_map;
1076  if(precedence_map.empty()) {
1077  int n = 0;
1078  precedence_map["not"] = ++n;
1079  precedence_map["where"] = ++n;
1080  precedence_map["or"] = ++n;
1081  precedence_map["and"] = ++n;
1082  precedence_map["="] = ++n;
1083  precedence_map["!="] = n;
1084  precedence_map["<"] = n;
1085  precedence_map[">"] = n;
1086  precedence_map["<="] = n;
1087  precedence_map[">="] = n;
1088  precedence_map["in"] = n;
1089  precedence_map["~"] = ++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["d"] = ++n;
1098  precedence_map["."] = ++n;
1099  }
1100 
1101  assert(precedence_map.count(std::string(t.begin, t.end)));
1102  return precedence_map[std::string(t.begin, t.end)];
1103 }
1104 
1105 static void parse_function_args(const tk::token* &i1, const tk::token* i2, std::vector<std::string>* res)
1106 {
1107  const tk::token* begin = i1, *end = i2; // These are used for error reporting
1108 
1109  if(i1->type == tk::TOKEN_LPARENS) {
1110  ++i1;
1111  } else {
1112  throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1113  }
1114 
1115  while((i1-> type != tk::TOKEN_RPARENS) && (i1 != i2)) {
1116  if(i1->type == tk::TOKEN_IDENTIFIER) {
1117  if(std::string((i1+1)->begin, (i1+1)->end) == "*") {
1118  res->push_back(std::string(i1->begin, i1->end) + std::string("*"));
1119  ++i1;
1120  } else {
1121  res->push_back(std::string(i1->begin, i1->end));
1122  }
1123  } else if(i1->type == tk::TOKEN_COMMA) {
1124  //do nothing
1125  } else {
1126  throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1127  }
1128 
1129  ++i1;
1130  }
1131 
1132  if(i1->type != tk::TOKEN_RPARENS) {
1133  throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1134  }
1135 
1136  ++i1;
1137 }
1138 
1139 static void parse_args(const tk::token* i1, const tk::token* i2,
1140  std::vector<expression_ptr>* res,
1141  function_symbol_table* symbols)
1142 {
1143  int parens = 0;
1144  const tk::token* beg = i1;
1145  while(i1 != i2) {
1146  if(i1->type == tk::TOKEN_LPARENS || i1->type == tk::TOKEN_LSQUARE ) {
1147  ++parens;
1148  } else if(i1->type == tk::TOKEN_RPARENS || i1->type == tk::TOKEN_RSQUARE ) {
1149  --parens;
1150  } else if(i1->type == tk::TOKEN_COMMA && !parens) {
1151  res->push_back(parse_expression(beg, i1, symbols));
1152  beg = i1+1;
1153  }
1154 
1155  ++i1;
1156  }
1157 
1158  if(beg != i1) {
1159  res->push_back(parse_expression(beg, i1, symbols));
1160  }
1161 }
1162 
1163 static void parse_set_args(const tk::token* i1, const tk::token* i2,
1164  std::vector<expression_ptr>* res,
1165  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_LPARENS || i1->type == tk::TOKEN_LSQUARE) {
1173  ++parens;
1174  } else if(i1->type == tk::TOKEN_RPARENS || i1->type == tk::TOKEN_RSQUARE) {
1175  --parens;
1176  } else if(i1->type == tk::TOKEN_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_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, 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_LPARENS || i1->type == tk::TOKEN_LSQUARE) {
1212  ++parens;
1213  } else if(i1->type == tk::TOKEN_RPARENS || i1->type == tk::TOKEN_RSQUARE) {
1214  --parens;
1215  } else if(!parens) {
1216  if(i1->type == tk::TOKEN_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_OPERATOR) {
1226  std::string op_name(i1->begin, i1->end);
1227 
1228  if(op_name == "=") {
1229  if(beg->type != tk::TOKEN_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_KEYWORD && (i1 + 1)->type == tk::TOKEN_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_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_LPARENS || i->type == tk::TOKEN_LSQUARE) {
1316  ++parens;
1317  } else if(i->type == tk::TOKEN_RPARENS || i->type == tk::TOKEN_RSQUARE) {
1318  --parens;
1319  } else if(parens == 0 && i->type == tk::TOKEN_OPERATOR) {
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_LPARENS && (i2-1)->type == tk::TOKEN_RPARENS) {
1334  return parse_expression(i1+1,i2-1,symbols);
1335  } else if((i2-1)->type == tk::TOKEN_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_LSQUARE && (i1+1)->type == tk::TOKEN_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_LSQUARE || square_parens) && tok != i1) {
1345  if(tok->type == tk::TOKEN_RSQUARE) {
1346  square_parens++;
1347  } else if(tok->type == tk::TOKEN_LSQUARE) {
1348  square_parens--;
1349  } else if((tok->type == tk::TOKEN_POINTER) && !square_parens ) {
1350  is_map = true;
1351  }
1352  --tok;
1353  }
1354 
1355  if(tok->type == tk::TOKEN_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_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_IDENTIFIER) {
1385  return std::make_shared<identifier_expression>(std::string(i1->begin, i1->end));
1386  } else if(i1->type == tk::TOKEN_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_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_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_IDENTIFIER &&
1419  (i1+1)->type == tk::TOKEN_LPARENS &&
1420  (i2-1)->type == tk::TOKEN_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_LPARENS) {
1426  ++nleft;
1427  } else if(i->type == tk::TOKEN_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
square_bracket_expression(expression_ptr left, expression_ptr key)
Definition: formula.cpp:668
variant get_value(const std::string &key) const
Definition: formula.cpp:514
TOKEN_TYPE
TOKEN_TYPE is already defined in a Winnt.h (a windows header which is included under some conditions...
Definition: tokenizer.hpp:27
variant execute(const formula_callable &, formula_debugger *) const
Definition: formula.cpp:308
void get_inputs(formula_input_vector &inputs) const
Definition: formula.cpp:826
string_callable(const variant &string)
Definition: formula.cpp:447
expression_ptr right_
Definition: formula.cpp:805
bool is_map() const
Definition: variant.hpp:67
variant get_value(const std::string &key) const
Definition: formula.cpp:574
std::string str() const
Definition: formula.cpp:625
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:39
std::string formula
Definition: formula.hpp:106
std::vector< expression_ptr > items_
Definition: formula.cpp:340
std::string evaluate_formula_impl(const std::string &)
Definition: formula.cpp:38
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:40
std::string filename
Definition: formula.hpp:107
int as_int() const
Definition: variant.cpp:292
#define a
expression_ptr right_
Definition: formula.cpp:662
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:914
bool is_string() const
Definition: variant.hpp:66
bool is_callable() const
Definition: variant.hpp:64
static void body(LexState *ls, expdesc *e, int ismethod, int line)
Definition: lparser.cpp:978
std::vector< substitution > subs_
Definition: formula.cpp:1066
std::map< std::string, variant > expr_table_evaluated
Definition: formula.cpp:60
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:426
const std::string & str() const
Definition: formula.hpp:72
std::vector< formula_input > formula_input_vector
map_expression(const std::vector< expression_ptr > &items)
Definition: formula.cpp:363
formula_debugger * debugger_
Definition: formula.cpp:824
std::string str() const
Definition: formula.cpp:906
const formula_callable & local_
Definition: formula.cpp:600
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:874
virtual std::string str() const
Definition: formula.cpp:367
virtual std::string str() const
Definition: formula.cpp:418
const std::vector< std::string > items
std::string str() const
Definition: formula.cpp:927
list_expression(const std::vector< expression_ptr > &items)
Definition: formula.cpp:324
expression_ptr body_
Definition: formula.cpp:871
std::shared_ptr< formula_expression > expression_ptr
Definition: formula.hpp:28
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:680
#define ERR_NG
Definition: formula.cpp:32
variant get_value(const std::string &key) const
Definition: formula.cpp:458
bool is_list() const
Definition: variant.hpp:65
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:329
variant get_value(const std::string &key) const
Definition: formula.cpp:833
const std::string & as_string() const
Definition: variant.cpp:319
std::string str_
Definition: formula.hpp:81
static formula_ptr create_optional_formula(const std::string &str, function_symbol_table *symbols=nullptr)
Definition: formula.cpp:250
static lg::log_domain log_engine("engine")
function_symbol_table * symbols_
Definition: formula.hpp:85
static void parse_where_clauses(const tk::token *i1, const tk::token *i2, expr_table_ptr res, function_symbol_table *symbols)
Definition: formula.cpp:1202
virtual std::string str() const
Definition: formula.cpp:302
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:733
std::vector< expression_ptr > items_
Definition: formula.cpp:399
struct utils::detail::formula_initer init
std::string str() const
Definition: formula.cpp:725
unary_operator_expression(const std::string &op, expression_ptr arg)
Definition: formula.cpp:405
where_expression(expression_ptr body, expr_table_ptr clauses)
Definition: formula.cpp:854
list_callable(const variant &list)
Definition: formula.cpp:504
void get_inputs(formula_input_vector &inputs) const
Definition: formula.cpp:449
where_variables(const formula_callable &base, expr_table_ptr table, formula_debugger *fdb)
Definition: formula.cpp:811
std::string type
Definition: formula.hpp:105
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:633
variant list_elements_add(const variant &v) const
Definition: variant.cpp:537
static std::shared_ptr< function_symbol_table > get_builtins()
Definition: function.cpp:1464
std::string str() const
Definition: formula.cpp:672
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:1039
static int operator_precedence(const tk::token &t)
Functions to handle the actual parsing of WFL.
Definition: formula.cpp:1073
const terrain_code NOT
variant list_elements_mul(const variant &v) const
Definition: variant.cpp:549
decimal_expression(int i, int f)
Definition: formula.cpp:925
lu_byte right
Definition: lparser.cpp:1227
variant execute(const formula_callable &, formula_debugger *) const
Definition: formula.cpp:938
std::string(* evaluate_formula)(const std::string &formula)
variant build_range(const variant &v) const
Definition: variant.cpp:587
std::map< std::string, expression_ptr > expr_table
Definition: formula.cpp:59
std::string string_cast() const
Definition: variant.cpp:638
const_formula_callable_ptr as_callable() const
Definition: variant.hpp:82
variant execute(const formula_callable &variables, formula_debugger *) const
Definition: formula.cpp:893
const std::string * filename
Definition: tokenizer.hpp:58
variant list_elements_div(const variant &v) const
Definition: variant.cpp:555
void get_inputs(formula_input_vector &inputs) const
Definition: formula.cpp:546
void get_inputs(formula_input_vector &inputs) const
Definition: formula.cpp:506
std::size_t i
Definition: function.cpp:934
operator_expression(const std::string &op, expression_ptr left, expression_ptr right)
Definition: formula.cpp:697
token get_token(iterator &i1, const iterator i2)
Definition: tokenizer.cpp:43
function_list_expression(function_symbol_table *symbols)
Definition: formula.cpp:298
static void expr(LexState *ls, expdesc *v)
Definition: lparser.cpp:1278
static map_location::DIRECTION s
const formula_callable & base_
Definition: formula.cpp:821
CURSOR_TYPE get()
Definition: cursor.cpp:215
std::string str() const
Definition: formula.cpp:81
std::string str() const
Definition: formula.cpp:858
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:51
const std::shared_ptr< function_symbol_table > managed_symbols_
Definition: formula.hpp:84
expr_table_ptr clauses_
Definition: formula.cpp:872
std::shared_ptr< const formula > const_formula_ptr
Definition: formula_fwd.hpp:23
void get_inputs(formula_input_vector &inputs) const
Definition: formula.cpp:602
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:60
static int dice_roll(int num_rolls, int faces)
Definition: formula.cpp:789
static void parse_set_args(const tk::token *i1, const tk::token *i2, std::vector< expression_ptr > *res, function_symbol_table *symbols)
Definition: formula.cpp:1163
string_expression(std::string str)
Definition: formula.cpp:949
formula(const std::string &str, function_symbol_table *symbols=nullptr)
Definition: formula.cpp:99
variant execute(const formula_callable &, formula_debugger *) const
Definition: formula.cpp:87
std::string str() const
Definition: formula.cpp:887
static std::string tokens_to_string(const tk::token *i1, const tk::token *i2)
Definition: formula.cpp:65
std::string str() const
Definition: formula.cpp:342
expression_ptr expr_
Definition: formula.hpp:80
variant concatenate(const variant &v) const
Definition: variant.cpp:561
std::string str() const
Definition: formula.cpp:1005
variant query_value(const std::string &key) const
Definition: callable.hpp:49
#define f
double t
Definition: astarsearch.cpp:64
Definition: contexts.hpp:43
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:1139
expression_ptr create_function(const std::string &fn, const std::vector< expression_ptr > &args) const
Definition: function.cpp:1432
Standard logging facilities (interface).
expr_table_evaluated evaluated_table_
Definition: formula.cpp:823
std::string message
Definition: exceptions.hpp:29
void add_function(const std::string &name, formula_function_ptr &&fcn)
Definition: function.cpp:1427
expr_table_ptr table_
Definition: formula.cpp:822
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:387
Classes that encapsulate and handle the various formula functions.
Definition: formula.cpp:295
#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:61
variant get_member(const std::string &name) const
Definition: variant.cpp:279
variant execute(const formula_callable &variables, formula_debugger *fdb=nullptr) const
Definition: formula.cpp:259
function_symbol_table * symbols_
Definition: formula.cpp:318
dot_callable(const formula_callable &global, const formula_callable &local)
Definition: formula.cpp:595
variant get_value(const std::string &key) const
Definition: formula.cpp:607
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:314
mock_char c
dot_expression(expression_ptr left, expression_ptr right)
Definition: formula.cpp:621
expression_ptr parse_expression(const tk::token *i1, const tk::token *i2, function_symbol_table *symbols)
Definition: formula.cpp:1263
static map_location::DIRECTION n
map_callable(const variant &map)
Definition: formula.cpp:544
static void parse_function_args(const tk::token *&i1, const tk::token *i2, std::vector< std::string > *res)
Definition: formula.cpp:1105
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
expression_ptr expr_
Definition: function.cpp:721
identifier_expression(const std::string &id)
Definition: formula.cpp:885
virtual void get_inputs(formula_input_vector &) const
Definition: callable.hpp:69
variant list_elements_sub(const variant &v) const
Definition: variant.cpp:543
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:21