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