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