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