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