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) {
766  res += (randomness::generator->next_random() % faces) + 1;
767  } else {
768  res += (rand() % faces) + 1;
769  }
770  }
771 
772  return res;
773  }
774 
775  //In some cases a IN or CAT macros are defined.
776  enum OP { AND, OR, NEQ, LTE, GTE, OP_CAT, OP_IN, GT='>', LT='<', EQ='=', RAN='~',
777  ADD='+', SUB='-', MUL='*', DIV='/', ADDL, SUBL, MULL, DIVL, DICE='d', POW='^', MOD='%' };
778 
782 };
783 
785 {
786 public:
788  : formula_callable(false)
789  , base_(base)
790  , table_(table)
791  , evaluated_table_()
792  , debugger_(fdb)
793  {
794  }
795 
796 private:
801 
803  {
804  for(expr_table::const_iterator i = table_->begin(); i != table_->end(); ++i) {
805  add_input(inputs, i->first);
806  }
807  }
808 
809  variant get_value(const std::string& key) const
810  {
811  expr_table::iterator i = table_->find(key);
812  if(i != table_->end()) {
813  expr_table_evaluated::const_iterator ev = evaluated_table_.find(key);
814  if(ev != evaluated_table_.end()) {
815  return ev->second;
816  }
817 
818  variant v = i->second->evaluate(base_, add_debug_info(debugger_, 0, "where[" + key + "]"));
819  evaluated_table_[key] = v;
820  return v;
821  }
822 
823  return base_.query_value(key);
824  }
825 };
826 
828 {
829 public:
831  : body_(body), clauses_(clauses)
832  {}
833 
834  std::string str() const
835  {
836  std::stringstream s;
837  s << "{where:(";
838  s << body_->str();
839  for(const expr_table::value_type &a : *clauses_) {
840  s << ", [" << a.first << "] -> ["<< a.second->str()<<"]";
841  }
842  s << ")}";
843  return s.str();
844  }
845 
846 private:
849 
850  variant execute(const formula_callable& variables,formula_debugger*fdb) const
851  {
852  where_variables wrapped_variables(variables, clauses_, fdb);
853  return body_->evaluate(wrapped_variables, add_debug_info(fdb, 0, "... where"));
854  }
855 };
856 
857 
859 {
860 public:
861  explicit identifier_expression(const std::string& id) : id_(id) {}
862 
863  std::string str() const
864  {
865  return id_;
866  }
867 
868 private:
869  variant execute(const formula_callable& variables, formula_debugger* /*fdb*/) const
870  {
871  return variables.query_value(id_);
872  }
873 
875 };
876 
878 {
879 public:
880  explicit integer_expression(int i) : i_(i) {}
881 
882  std::string str() const
883  {
884  std::stringstream s;
885  s << i_;
886  return s.str();
887  }
888 
889 private:
890  variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
891  {
892  return variant(i_);
893  }
894 
895  int i_;
896 };
897 
899 {
900 public:
901  decimal_expression(int i, int f) : i_(i), f_(f) {}
902 
903  std::string str() const
904  {
905  std::stringstream s;
906  s << i_ << '.';
907  s.width(3);
908  s.fill('0');
909  s << f_;
910  return s.str();
911  }
912 
913 private:
914  variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
915  {
916  return variant(i_ * 1000 + f_, variant::DECIMAL_VARIANT );
917  }
918 
919  int i_, f_;
920 };
921 
923 {
924 public:
926  : str_()
927  , subs_()
928  {
929  std::string::iterator i = str.begin();
930  while((i = std::find(i, str.end(), '[')) != str.end()) {
931  int bracket_depth = 0;
932  std::string::iterator j = i + 1;
933  while(j != str.end() && (bracket_depth > 0 || *j != ']')) {
934  if(*j == '[') {
935  bracket_depth++;
936  } else if(*j == ']' && bracket_depth > 0) {
937  bracket_depth--;
938  }
939  ++j;
940  }
941 
942  if(j == str.end()) {
943  break;
944  }
945 
946  const std::string formula_str(i+1, j);
947  const int pos = i - str.begin();
948  if(j - i == 2 && (i[1] == '(' || i[1] == '\'' || i[1] == ')')) {
949  // Bracket contained nothing but a quote or parenthesis.
950  // This means it was intended as a literal quote or square bracket.
951  i = str.erase(i);
952  if(*i == '(') {
953  *i = '[';
954  } else if(*i == ')') {
955  *i = ']';
956  }
957 
958  i = str.erase(i + 1);
959  continue;
960  } else {
961  i = str.erase(i, j+1);
962  }
963 
964  substitution sub;
965  sub.pos = pos;
966  try {
967  sub.calculation.reset(new formula(formula_str));
968  } catch(formula_error& e) {
969  e.filename += " - string substitution";
970  throw e;
971  }
972 
973  subs_.push_back(sub);
974  }
975 
976  std::reverse(subs_.begin(), subs_.end());
977 
978  str_ = variant(str);
979  }
980 
981  std::string str() const
982  {
983  std::string res = str_.as_string();
984  int j = res.size() - 1;
985 
986  for(const auto& sub : subs_) {
987  for(; j >= sub.pos && j >= 0; j--) {
988  if(res[j] == '\'') {
989  res.replace(j, 1, "[']");
990  } else if(res[j] == '[') {
991  res.replace(j, 1, "[(]");
992  } else if(res[j] == ']') {
993  res.replace(j, 1, "[)]");
994  }
995  }
996 
997  const std::string str = "[" + sub.calculation->str() + "]";
998  res.insert(sub.pos, str);
999  }
1000 
1001  for(; j >= 0; j--) {
1002  if(res[j] == '\'') {
1003  res.replace(j, 1, "[']");
1004  } else if(res[j] == '[') {
1005  res.replace(j, 1, "[(]");
1006  } else if(res[j] == ']') {
1007  res.replace(j, 1, "[)]");
1008  }
1009  }
1010 
1011  return "'" + res + "'";
1012  }
1013 
1014 private:
1015  variant execute(const formula_callable& variables, formula_debugger*fdb) const
1016  {
1017  if(subs_.empty()) {
1018  return str_;
1019  }
1020 
1021  std::string res = str_.as_string();
1022  for(size_t i = 0; i < subs_.size(); ++i) {
1023  const int j = subs_.size() - i - 1;
1024  const substitution& sub = subs_[i];
1025  add_debug_info(fdb, j, "[string subst]");
1026  const std::string str = sub.calculation->evaluate(variables,fdb).string_cast();
1027  res.insert(sub.pos, str);
1028  }
1029 
1030  return variant(res);
1031  }
1032 
1034  {
1036 
1037  int pos;
1039  };
1040 
1042  std::vector<substitution> subs_;
1043 };
1044 
1045 
1046 /**
1047  * Functions to handle the actual parsing of WFL.
1048  */
1049 static int operator_precedence(const tk::token& t)
1050 {
1051  static std::map<std::string,int> precedence_map;
1052  if(precedence_map.empty()) {
1053  int n = 0;
1054  precedence_map["not"] = ++n;
1055  precedence_map["where"] = ++n;
1056  precedence_map["or"] = ++n;
1057  precedence_map["and"] = ++n;
1058  precedence_map["="] = ++n;
1059  precedence_map["!="] = n;
1060  precedence_map["<"] = n;
1061  precedence_map[">"] = n;
1062  precedence_map["<="] = n;
1063  precedence_map[">="] = n;
1064  precedence_map["in"] = n;
1065  precedence_map["~"] = ++n;
1066  precedence_map["+"] = ++n;
1067  precedence_map["-"] = n;
1068  precedence_map[".."] = n;
1069  precedence_map["*"] = ++n;
1070  precedence_map["/"] = n;
1071  precedence_map["%"] = ++n;
1072  precedence_map["^"] = ++n;
1073  precedence_map["d"] = ++n;
1074  precedence_map["."] = ++n;
1075  }
1076 
1077  assert(precedence_map.count(std::string(t.begin, t.end)));
1078  return precedence_map[std::string(t.begin, t.end)];
1079 }
1080 
1081 static void parse_function_args(const tk::token* &i1, const tk::token* i2, std::vector<std::string>* res)
1082 {
1083  const tk::token* begin = i1, *end = i2; // These are used for error reporting
1084 
1085  if(i1->type == tk::TOKEN_LPARENS) {
1086  ++i1;
1087  } else {
1088  throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1089  }
1090 
1091  while((i1-> type != tk::TOKEN_RPARENS) && (i1 != i2)) {
1092  if(i1->type == tk::TOKEN_IDENTIFIER) {
1093  if(std::string((i1+1)->begin, (i1+1)->end) == "*") {
1094  res->push_back(std::string(i1->begin, i1->end) + std::string("*"));
1095  ++i1;
1096  } else {
1097  res->push_back(std::string(i1->begin, i1->end));
1098  }
1099  } else if(i1->type == tk::TOKEN_COMMA) {
1100  //do nothing
1101  } else {
1102  throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1103  }
1104 
1105  ++i1;
1106  }
1107 
1108  if(i1->type != tk::TOKEN_RPARENS) {
1109  throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1110  }
1111 
1112  ++i1;
1113 }
1114 
1115 static void parse_args(const tk::token* i1, const tk::token* i2,
1116  std::vector<expression_ptr>* res,
1117  function_symbol_table* symbols)
1118 {
1119  int parens = 0;
1120  const tk::token* beg = i1;
1121  while(i1 != i2) {
1122  if(i1->type == tk::TOKEN_LPARENS || i1->type == tk::TOKEN_LSQUARE ) {
1123  ++parens;
1124  } else if(i1->type == tk::TOKEN_RPARENS || i1->type == tk::TOKEN_RSQUARE ) {
1125  --parens;
1126  } else if(i1->type == tk::TOKEN_COMMA && !parens) {
1127  res->push_back(parse_expression(beg, i1, symbols));
1128  beg = i1+1;
1129  }
1130 
1131  ++i1;
1132  }
1133 
1134  if(beg != i1) {
1135  res->push_back(parse_expression(beg, i1, symbols));
1136  }
1137 }
1138 
1139 static void parse_set_args(const tk::token* i1, const tk::token* i2,
1140  std::vector<expression_ptr>* res,
1141  function_symbol_table* symbols)
1142 {
1143  int parens = 0;
1144  bool check_pointer = false;
1145  const tk::token* beg = i1;
1146  const tk::token* begin = i1, *end = i2; // These are used for error reporting
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_POINTER && !parens ) {
1153  if(!check_pointer) {
1154  check_pointer = true;
1155  res->push_back(parse_expression(beg, i1, symbols));
1156  beg = i1+1;
1157  } else {
1158  throw formula_error("Too many '->' operators found", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1159  }
1160  } else if(i1->type == tk::TOKEN_COMMA && !parens ) {
1161  if(check_pointer)
1162  check_pointer = false;
1163  else {
1164  throw formula_error("Expected comma, but '->' found", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1165  }
1166  res->push_back(parse_expression(beg, i1, symbols));
1167  beg = i1+1;
1168  }
1169 
1170  ++i1;
1171  }
1172 
1173  if(beg != i1) {
1174  res->push_back(parse_expression(beg, i1, symbols));
1175  }
1176 }
1177 
1178 static void parse_where_clauses(const tk::token* i1, const tk::token* i2, expr_table_ptr res, function_symbol_table* symbols)
1179 {
1180  int parens = 0;
1181  const tk::token* original_i1_cached = i1;
1182  const tk::token* beg = i1;
1183  const tk::token* begin = i1, *end = i2; // These are used for error reporting
1184  std::string var_name;
1185 
1186  while(i1 != i2) {
1187  if(i1->type == tk::TOKEN_LPARENS) {
1188  ++parens;
1189  } else if(i1->type == tk::TOKEN_RPARENS) {
1190  --parens;
1191  } else if(!parens) {
1192  if(i1->type == tk::TOKEN_COMMA) {
1193  if(var_name.empty()) {
1194  throw formula_error("There is 'where <expression>' but 'where name=<expression>' was needed",
1195  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1196  }
1197 
1198  (*res)[var_name] = parse_expression(beg, i1, symbols);
1199  beg = i1+1;
1200  var_name = "";
1201  } else if(i1->type == tk::TOKEN_OPERATOR) {
1202  std::string op_name(i1->begin, i1->end);
1203 
1204  if(op_name == "=") {
1205  if(beg->type != tk::TOKEN_IDENTIFIER) {
1206  if(i1 == original_i1_cached) {
1207  throw formula_error("There is 'where <expression' but 'where name=<expression>' was needed",
1208  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1209  } else {
1210  throw formula_error("There is 'where <expression>=<expression>' but 'where name=<expression>' was needed",
1211  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1212  }
1213  } else if(beg+1 != i1) {
1214  throw formula_error("There is 'where name <expression>=<expression>' but 'where name=<expression>' was needed",
1215  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1216  } else if(!var_name.empty()) {
1217  throw formula_error("There is 'where name=name=<expression>' but 'where name=<expression>' was needed",
1218  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1219  }
1220 
1221  var_name.insert(var_name.end(), beg->begin, beg->end);
1222  beg = i1+1;
1223  }
1224  }
1225  }
1226  ++i1;
1227  }
1228 
1229  if(beg != i1) {
1230  if(var_name.empty()) {
1231  throw formula_error("There is 'where <expression>' but 'where name=<expression>' was needed",
1232  tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1233  }
1234 
1235  (*res)[var_name] = parse_expression(beg, i1, symbols);
1236  }
1237 }
1238 
1240 {
1241  if(i1 == i2) {
1242  throw formula_error("Empty expression", "", *i1->filename, i1->line_number);
1243  }
1244 
1245  std::unique_ptr<function_symbol_table> temp_functions;
1246  if(!symbols) {
1247  temp_functions.reset(new function_symbol_table(function_symbol_table::get_builtins()));
1248  symbols = temp_functions.get();
1249  }
1250 
1251  const tk::token* begin = i1, *end = i2; // These are used for error reporting
1252 
1253  if(i1->type == tk::TOKEN_KEYWORD && (i1 + 1)->type == tk::TOKEN_IDENTIFIER) {
1254  if(std::string(i1->begin, i1->end) == "def") {
1255  ++i1;
1256  const std::string formula_name = std::string(i1->begin, i1->end);
1257 
1258  std::vector<std::string> args;
1259  parse_function_args(++i1, i2, &args);
1260 
1261  const tk::token* beg = i1;
1262  while((i1 != i2) && (i1->type != tk::TOKEN_SEMICOLON)) {
1263  ++i1;
1264  }
1265 
1266  const std::string precond = "";
1267  if(symbols == nullptr) {
1268  throw formula_error("Function symbol table required but not present", "",*i1->filename, i1->line_number);
1269  }
1270 
1271  symbols->add_function(formula_name,
1274  formula_name, const_formula_ptr(new formula(beg, i1, symbols)),
1275  formula::create_optional_formula(precond, symbols), args
1276  )
1277  )
1278  );
1279 
1280  if((i1 == i2) || (i1 == (i2-1))) {
1281  return expression_ptr(new function_list_expression(symbols));
1282  } else {
1283  return parse_expression((i1+1), i2, symbols);
1284  }
1285  }
1286  }
1287 
1288  int parens = 0;
1289  const tk::token* op = nullptr;
1290  bool operator_group = false;
1291 
1292  for(const tk::token* i = i1; i != i2; ++i) {
1293  if(i->type == tk::TOKEN_LPARENS || i->type == tk::TOKEN_LSQUARE) {
1294  ++parens;
1295  } else if(i->type == tk::TOKEN_RPARENS || i->type == tk::TOKEN_RSQUARE) {
1296  --parens;
1297  } else if(parens == 0 && i->type == tk::TOKEN_OPERATOR) {
1298  if((!operator_group ) && (op == nullptr || operator_precedence(*op) >= operator_precedence(*i))) {
1299  // Need special exception for exponentiation to be right-associative
1300  if(*i->begin != '^' || op == nullptr || *op->begin != '^') {
1301  op = i;
1302  }
1303  }
1304  operator_group = true;
1305  } else {
1306  operator_group = false;
1307  }
1308  }
1309 
1310  if(op == nullptr) {
1311  if(i1->type == tk::TOKEN_LPARENS && (i2-1)->type == tk::TOKEN_RPARENS) {
1312  return parse_expression(i1+1,i2-1,symbols);
1313  } else if((i2-1)->type == tk::TOKEN_RSQUARE) { //check if there is [ ] : either a list/map definition, or a operator
1314  // First, a special case for an empty map
1315  if(i2 - i1 == 3 && i1->type == tk::TOKEN_LSQUARE && (i1+1)->type == tk::TOKEN_POINTER) {
1316  return expression_ptr(new map_expression(std::vector<expression_ptr>()));
1317  }
1318 
1319  const tk::token* tok = i2-2;
1320  int square_parens = 0;
1321  bool is_map = false;
1322  while ((tok->type != tk::TOKEN_LSQUARE || square_parens) && tok != i1) {
1323  if(tok->type == tk::TOKEN_RSQUARE) {
1324  square_parens++;
1325  } else if(tok->type == tk::TOKEN_LSQUARE) {
1326  square_parens--;
1327  } else if((tok->type == tk::TOKEN_POINTER) && !square_parens ) {
1328  is_map = true;
1329  }
1330  --tok;
1331  }
1332 
1333  if(tok->type == tk::TOKEN_LSQUARE) {
1334  if(tok == i1) {
1335  // Create a list or a map
1336  std::vector<expression_ptr> args;
1337 
1338  if( is_map ) {
1339  parse_set_args(i1+1, i2-1, &args, symbols);
1340  return expression_ptr(new map_expression(args));
1341  } else {
1342  parse_args(i1+1,i2-1,&args,symbols);
1343  return expression_ptr(new list_expression(args));
1344  }
1345  } else {
1346  // Execute operator [ ]
1347  try{
1348  return expression_ptr(
1350  parse_expression(i1, tok, symbols),
1351  parse_expression(tok + 1, i2 - 1, symbols)
1352  )
1353  );
1354  } catch (formula_error& e){
1355  throw formula_error( e.type, tokens_to_string(i1, i2-1), *i1->filename, i1->line_number );
1356  }
1357  }
1358  }
1359  } else if(i2 - i1 == 1) {
1360  if(i1->type == tk::TOKEN_KEYWORD) {
1361  if(std::string(i1->begin, i1->end) == "functions") {
1362  return expression_ptr(new function_list_expression(symbols));
1363  }
1364  } else if(i1->type == tk::TOKEN_IDENTIFIER) {
1365  return expression_ptr(new identifier_expression(std::string(i1->begin, i1->end)));
1366  } else if(i1->type == tk::TOKEN_INTEGER) {
1367  int n = std::stoi(std::string(i1->begin, i1->end));
1368  return expression_ptr(new integer_expression(n));
1369  } else if(i1->type == tk::TOKEN_DECIMAL) {
1370  tk::iterator dot = i1->begin;
1371  while(*dot != '.') {
1372  ++dot;
1373  }
1374 
1375  int n = std::stoi(std::string(i1->begin,dot));
1376 
1377  tk::iterator literal_end = i1->end;
1378 
1379  if(literal_end - dot > 4) {
1380  literal_end = dot + 4;
1381  }
1382 
1383  ++dot;
1384 
1385  int f = 0;
1386 
1387  int multiplicator = 100;
1388  while(dot != literal_end) {
1389  f += (*dot - 48) * multiplicator;
1390  multiplicator /= 10;
1391  ++dot;
1392  }
1393 
1394  return expression_ptr(new decimal_expression(n, f));
1395  } else if(i1->type == tk::TOKEN_STRING_LITERAL) {
1396  return expression_ptr(new string_expression(std::string(i1->begin + 1, i1->end - 1)));
1397  }
1398  } else if(i1->type == tk::TOKEN_IDENTIFIER &&
1399  (i1+1)->type == tk::TOKEN_LPARENS &&
1400  (i2-1)->type == tk::TOKEN_RPARENS)
1401  {
1402  const tk::token* function_call_begin = i1, *function_call_end = i2; // These are used for error reporting
1403  int nleft = 0, nright = 0;
1404  for(const tk::token* i = i1; i != i2; ++i) {
1405  if(i->type == tk::TOKEN_LPARENS) {
1406  ++nleft;
1407  } else if(i->type == tk::TOKEN_RPARENS) {
1408  ++nright;
1409  }
1410  }
1411 
1412  if(nleft == nright) {
1413  std::vector<expression_ptr> args;
1414  parse_args(i1+2,i2-1,&args,symbols);
1415  try{
1416  return symbols->create_function(std::string(i1->begin, i1->end),args);
1417  }
1418  catch(formula_error& e) {
1419  throw formula_error(e.type, tokens_to_string(function_call_begin, function_call_end), *i1->filename, i1->line_number);
1420  }
1421  }
1422  }
1423 
1424  throw formula_error("Could not parse expression", tokens_to_string(i1, i2), *i1->filename, i1->line_number);
1425  }
1426 
1427  if(op + 1 == end) {
1428  throw formula_error("Expected another token", tokens_to_string(begin, end - 1), *op->filename, op->line_number);
1429  }
1430 
1431  if(op == i1) {
1432  try{
1433  return expression_ptr(
1434  new unary_operator_expression(std::string(op->begin, op->end), parse_expression(op + 1, i2 ,symbols)));
1435  } catch(formula_error& e) {
1436  throw formula_error( e.type, tokens_to_string(begin,end - 1), *op->filename, op->line_number);
1437  }
1438  }
1439 
1440  const std::string op_name(op->begin,op->end);
1441 
1442  if(op_name == ".") {
1443  return expression_ptr(
1444  new dot_expression(
1445  parse_expression(i1, op, symbols),
1446  parse_expression(op + 1,i2, symbols)
1447  )
1448  );
1449  }
1450 
1451  if(op_name == "where") {
1452  expr_table_ptr table(new expr_table());
1453  parse_where_clauses(op+1, i2, table, symbols);
1454 
1455  return expression_ptr(new where_expression(parse_expression(i1, op, symbols), table));
1456  }
1457 
1458  return expression_ptr(
1459  new operator_expression(op_name,
1460  parse_expression(i1, op, symbols),
1461  parse_expression(op + 1, i2, symbols)
1462  )
1463  );
1464 }
1465 
1466 } // 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:863
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:882
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:781
std::vector< expression_ptr > items_
Definition: formula.cpp:312
void add_function(const std::string &name, formula_function_ptr fcn)
Definition: function.cpp:1661
std::set< std::string > get_function_names() const
Definition: function.cpp:1683
variant execute(const formula_callable &, formula_debugger *) const
Definition: formula.cpp:914
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:781
expression_ptr create_function(const std::string &fn, const std::vector< expression_ptr > &args) const
Definition: function.cpp:1666
static void body(LexState *ls, expdesc *e, int ismethod, int line)
Definition: lparser.cpp:782
std::vector< substitution > subs_
Definition: formula.cpp:1042
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:1015
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:800
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
std::shared_ptr< formula_function > formula_function_ptr
Definition: function.hpp:138
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:847
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:903
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:1178
variant get_value(const std::string &key) const
Definition: formula.cpp:809
std::string str() const
Definition: formula.cpp:834
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:890
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:830
variant execute(const formula_callable &variables, formula_debugger *) const
Definition: formula.cpp:869
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:787
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:1695
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:1049
decimal_expression(int i, int f)
Definition: formula.cpp:901
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:802
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:797
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
CURSOR_TYPE get()
Definition: cursor.cpp:194
#define i
expr_table_ptr clauses_
Definition: formula.cpp:848
std::shared_ptr< const formula > const_formula_ptr
Definition: formula_fwd.hpp:23
std::string str() const
Definition: formula.cpp:981
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:1139
string_expression(std::string str)
Definition: formula.cpp:925
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:1115
bool is_string() const
Definition: variant.hpp:63
expr_table_evaluated evaluated_table_
Definition: formula.cpp:799
std::string message
Definition: exceptions.hpp:31
variant execute(const formula_callable &, formula_debugger *) const
Definition: formula.cpp:280
uint32_t next_random()
Provides the next random draw.
Definition: random.cpp:81
expr_table_ptr table_
Definition: formula.cpp:798
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:1239
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:1081
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
expression_ptr expr_
Definition: function.cpp:849
std::string str() const
Definition: formula.cpp:644
identifier_expression(const std::string &id)
Definition: formula.cpp:861
variant execute(const formula_callable &variables, formula_debugger *fdb) const
Definition: formula.cpp:850
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